Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>There are a few problems with the way you designed your application. First of all, you are calling the Ninject kernel directly from within your code. This is called the <a href="http://en.wikipedia.org/wiki/Service_locator_pattern" rel="nofollow noreferrer">Service Locator pattern</a> and <a href="http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx" rel="nofollow noreferrer">it is considered an anti-pattern</a>. It makes testing your application much harder and you are already experiencing this. You are trying to mock the Ninject container in your unit test, which complicates things tremendously.</p> <p>Next, you are injecting primitive types (<code>string</code>, <code>bool</code>) in the constructor of your <code>DirEnum</code> type. I like how MNrydengren states it in the comments:</p> <blockquote> <p>take "compile-time" dependencies through constructor parameters and "run-time" dependencies through method parameters</p> </blockquote> <p>It's hard for me to guess what that class should do, but since you are injecting these variables that change at run-time into the <code>DirEnum</code> constructor, you end up with a hard to test application.</p> <p>There are multiple ways to fix this. Two that come in mind are the use of method injection and the use of a factory. Which one is feasible is up to you.</p> <p>Using method injection, your <code>Configurator</code> class will look like this:</p> <pre class="lang-cs prettyprint-override"><code>class Configurator { private readonly IDirEnum dirEnum; // Injecting IDirEnum through the constructor public Configurator(IDirEnum dirEnum) { this.dirEnum = dirEnum; } public ConfigureServices(string[] args) { var parser = new ArgParser(args); // Inject the arguments into a method this.dirEnum.SomeOperation( argParser.filePath argParser.fileFilter argParser.subDirs); } } </code></pre> <p>Using a factory, you would need to define a factory that knows how to create new <code>IDirEnum</code> types:</p> <pre class="lang-cs prettyprint-override"><code>interface IDirEnumFactory { IDirEnum CreateDirEnum(string filePath, string fileFilter, bool includeSubDirs); } </code></pre> <p>Your <code>Configuration</code> class can now depend on the <code>IDirEnumFactory</code> interface:</p> <pre class="lang-cs prettyprint-override"><code>class Configurator { private readonly IDirEnumFactory dirFactory; // Injecting the factory through the constructor public Configurator(IDirEnumFactory dirFactory) { this.dirFactory = dirFactory; } public ConfigureServices(string[] args) { var parser = new ArgParser(args); // Creating a new IDirEnum using the factory var dirEnum = this.dirFactory.CreateDirEnum( parser.filePath parser.fileFilter parser.subDirs); } } </code></pre> <p>See how in both examples the dependencies get injected into the <code>Configurator</code> class. This is called the <a href="http://en.wikipedia.org/wiki/Dependency_injection" rel="nofollow noreferrer">Dependency Injection pattern</a>, opposed to the Service Locator pattern, where the <code>Configurator</code> asks for its dependencies by calling into the Ninject kernel.</p> <p>Now, since your <code>Configurator</code> is completely free from any IoC container what so ever, you can now easily test this class, by injecting a mocked version of the dependency it expects.</p> <p>What is left is to configure the Ninject container in the top of your application (in DI terminology: the <a href="https://stackoverflow.com/questions/6277771/what-is-a-composition-root-in-the-context-of-dependency-injection">composition root</a>). With the method injection example, your container configuration would stay the same, with the factory example, you will need to replace the <code>Bind&lt;IDirEnum&gt;().To&lt;DirEnum&gt;()</code> line with something as follows:</p> <pre class="lang-cs prettyprint-override"><code>public static void Bootstrap() { kernel.Bind&lt;IDirEnumFactory&gt;().To&lt;DirEnumFactory&gt;(); } </code></pre> <p>Of course, you will need to create the <code>DirEnumFactory</code>:</p> <pre class="lang-cs prettyprint-override"><code>class DirEnumFactory : IDirEnumFactory { IDirEnum CreateDirEnum(string filePath, string fileFilter, bool includeSubDirs) { return new DirEnum(filePath, fileFilter, includeSubDirs); } } </code></pre> <p><strong>WARNING</strong>: Do note that factory abstractions are in most cases not the best design, as explained <a href="https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=100" rel="nofollow noreferrer">here</a>.</p> <p>The last thing you need to do is to create a new <code>Configurator</code> instance. You can simply do this as follows:</p> <pre class="lang-cs prettyprint-override"><code>public static Configurator CreateConfigurator() { return kernel.Get&lt;Configurator&gt;(); } public static void Main(string[] args) { Bootstrap(): var configurator = CreateConfigurator(); configurator.ConfigureServices(args); } </code></pre> <p>Here we call the kernel. Although calling the container directly should be prevented, there will always at least be one place in your application where you call the container, simply because it must wire everything up. However, we try to minimize the number of times the container is called directly, because it improves -among other things- the testability of our code.</p> <p>See how I didn't really answer your question, but showed a way to work around the problem very effectively.</p> <p>You might still want to test your DI configuration. That's very valid IMO. I do this in my applications. But for this, you often don't need the DI container, or even if your do, this doesn't mean that all your tests should have a dependency on the container. This relationship should only exist for the tests that test the DI configuration itself. Here is a test:</p> <pre class="lang-cs prettyprint-override"><code>[TestMethod] public void DependencyConfiguration_IsConfiguredCorrectly() { // Arrange Program.Bootstrap(); // Act var configurator = Program.CreateConfigurator(); // Assert Assert.IsNotNull(configurator); } </code></pre> <p>This test indirectly depends on Ninject and it will fail when Ninject is not able to construct a new <code>Configurator</code> instance. When you keep your constructors clean from any logic and only use it for storing the taken dependencies in private fields, you can run this, without the risk of calling out to a database, web service or what so ever.</p> <p>I hope this helps.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload