TDD and Ninject Modules
Following our commitment to Test Driven Development (TDD), we should not write a line of code before writing a failing test for it, right? yet, many people write, in an unconscious way, a lot of code without doing any kind of testing around it.
Ninject is an Open Source Dependency Injector for .NET and you should take a look at it if you haven’t.
Ninject allows us to manage the dependencies between our classes relieving us from this painful process.
We can simply configure Ninject by creating a Kernel and binding the dependencies.
Now, if we have a class that needs an instance of TIerface, Ninject will automatically provide an instance of Service.
Because we are following TDD principles, let’s assume that the class Service was created using this principle. However, we still need to configure the Kernel and bind the dependencies manually, without doing any test.
You might think that it is a straightforward detail, but we need to ask ourselves what could happen if we do it wrong. Binding services incorrectly can cause the application to crash, but we will only realize it once we have run it. In a worst scenario, the application won’t crash, but it will behave completely different from what we expected!
Wasn’t this some of reason behind TDD? Then why not to use it to avoid these problems?
Ninject Modules are the way to go if we need to segregate our dependencies. We might want to isolate how we compose our dependency graph in the module in charge of the graph. The composition root should be as dummy as possible since most of the time, we cannot test it at all.
A Ninject Module looks as follows:
In here, we have an interface ISelector being bound to its implementation RoundRobinPathSelector. We have separated this code in a different part of the application. In fact, it is in a library, completely isolated, that only knows about RoundRobinPathSelector.
In order to use this binding, we load the module into the Ninject Kernel.
Now, if we ask for the kernel for an instance of ISelector, it will give us back a RoundRobinPathSelector.
All the binding process is quite dumb, but what happens if we need a second implementation of ISelector?
Let’s suppose we have a new class called BestPathSelector and we want to do a conditional binding on the kernel.
Let’s see how it could be done.
Now we have sent an argument to the module and based on it, we do the corresponding binding. The problem is that our module is not dummy anymore, we are taking decisions on it, but we are not testing at all! This part could be critical to our application’s behavior and we should have tested it before even writing it.
Fluent Assertions is a library for test verification and it has an extension for Ninject testing.
In our tests, we want to verify that the Ninject Kernel is doing the correct binding before using the kernel and see it crash.
Our tests could look like this.
Here we presented two tests, each verifying a different branch of the PathSelectorModule based on its arguments.
Only after this, we can assume the correctness of our module and the behavior it will bring to our application.
Ninject Mocking Kernel
Too many times, our modules depend on other modules that need to be loaded into the kernel before trying to resolve anything in our own module. Ninject offers extensions, so we could mock our kernel, so it resolves the additional dependencies we need in our module. This makes the testing process easier because we avoid wiring up all other dependencies we need.
For more information about Ninject Mocking Kernel, please, follow the Ninject Extensions link.
Test Driven Development should be embraced in all parts of our code. We cannot say we use TDD as if there are untested pieces that will cause hell in the future.
Testing Ninject bindings is now an easy task thanks to the Fluent Assertions library. Use it and enhance the quality of whatever you build.