Previously On My Blog
In my previous post, I altered a simple ASP.NET Web Forms application so that instead of using Poor Man’s Dependency Injection and making constructor calls directly to concrete types to an application depending solely on interfaces by using an Inversion of Control (IoC) container. You can download the source for the application here. In this post I am going to add a couple of testing projects to the application: a unit test project and an integration test project. Using IoC is great, but how can one be sure that it is working correctly? Running the application and making sure it does not get any resolution-related exceptions is one option, but is really not the best way to test the IoC. The preferred method would be to have automated tests assure that one’s classes are working properly with the usage of the IoC container. So it would be best if tests were created in one of our test projects.
Where Do the IoC Container Tests Go?
The next question that comes to mind is which test project do you test the IoC container? Putting them in unit tests is one option, but presents several problems. The IoC container in this instance is a static class. Static classes in general are much harder to test, as injecting fakes or mocks for the static class usage is difficult. When it comes to unit testing, all external dependencies should be mocked, faked, or somehow eliminated from the context of the test. In this project’s business logic implementation, all classes expose their dependencies through a single constructor. The IoC container automatically resolves these dependencies at runtime, but at test time it would be ideal to mock these dependencies out.
It sounds like unit tests are not the place to test the IoC Container. This leaves the developer with only one option: integration tests. Integration tests are a perfect candidate for IoC container testing. A typical integration test makes sure that a particular method or class works when all of the real pieces are in use (i.e. all of its dependencies are using what will be used in the application). Therefore, using the IoC container to resolve an instance of the class that is being tested is a logical way to assure that the container will correctly resolve an instance of the class. The application is dependent on this as well, and rather than letting the application bomb, the tests can instead fail, which will immediately expose any flaws in the container’s implementation.
A First Integration Test for a Business Logic Class
In order to begin testing, we need a test project. As was discussed above, the IoC container will be tested within the body of integration tests, so that will be the project to create. A reference to the NUnit framework has been added, as can be seen in the image below.
Next, a tester file needs created. In this example, the SuperRepository will be tested. Only one test will be written. This test will show that GetById does in fact return a SuperDomainObject with the given ID.
The ISuperRepository object is constructed by using the IoC container. Note that we have to EnsureDependenciesRegistered in order to make the call because the tester will not be calling Global.asax’s Application_Start() method. This test fixture has no knowledge of what implementation it’s actually using. It is using whatever IoC returns, which also happens to be what the application will depend on. Because of this, the integration test will always make sure that the implementation of ISuperRepository in use always does what it’s supposed to do, even if in the future a different implementation is to be used.
Running this test shows that the repository is doing its job correctly:
Suppose, however, that ISuperRepository was not registered with the IoC Container. Well, now the integration test will break, letting the developer know that this registration does not yet exist. This is a much better time to find out about this slip rather than waiting to run the application. In the case of Unity, and an exception is thrown:
“The current type, SuperWebApplication.Core.Interfaces.ISuperRepository, is an interface and cannot be constructed. Are you missing a type mapping?”
As a result, the test fails (it actually fails during setup since IoC is called during the test’s construction):
Added the resolution back to the Dependency Registrar fixes the problem and makes the test pass. Now there is a little more confidence built into the application that the dependencies are registered successfully.
IoC containers make dependency injection really nice, but the type mappings have to be set up correctly in order for IoC to be effective. Instead of letting IoC fail at runtime, tests can be written to use IoC in the same manner. Unit tests should not have to worry about how an implementation is reached, but rather that an implementation is set up correctly. This means that IoC should be tested only within the integration tests project. With integration tests set up to use the IoC container, they will be simulating how the classes will be called in the application itself. When the tests are passing, it shows that the IoC container is configured correctly and can be used.