Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use different configurations with Simple Injector

I'm using the Simple Injector Dependency Injection framework and it looks cool and nice. But after building a configuration and use it, now I want to know how to change from one configuration to another.

Scenario: Let's imagine I've set up a configuration in the Global Asax and I have the public and global Container instance there. Now I want to make some tests and I want them to use mock classes so I want to change the configuration.

I can, of course, build another configuration and assign it to the global Container created by default, so that every time I run a test the alternative configuration will be set. But on doing that and though I'm in development context the Container is changed for everyone, even for normal requests. I know I'm testing in this context and that shouldn't matter, but I have the feeling that this is not the way for doing this... and I wonder how to change from one configuration to another in the correct way.

like image 536
Ruben.Canton Avatar asked Dec 07 '12 17:12

Ruben.Canton


1 Answers

When doing unit tests, you shouldn't use the container at all. Just create the class under test by calling its constructor and supplying it with the proper mock objects.

One pattern that helped me out here a lot in the past is the use of a simple test class-specific factory method. This method centralizes the creation of the class under test and minimizes the amount of changes that need to be made when the dependencies of the class under test change. This is how such factory method could look like:

private ClassUnderTest CreateValidClassUnderTest(params object[] dependencies)
{
    return new ClassUnderTest(
        dependencies.OfType<ILogger>().SingleOrDefault() ?? new FakeLogger(),
        dependencies.OfType<IMailSender>().SingleOrDefault() ?? new FakeMailer(),
        dependencies.OfType<IEventPublisher>().SingleOrDefault() ?? new FakePublisher());
}

For integration tests it's much more common to use the container, and swap a few dependencies of the container. Still, those integration tests will not use the container that you created in your application_start, but each integration test will in that case most likely have its own new container instance, since each test should run in isolation. And even if you did use a single container from application_start, your integration tests are ran from a separate project and won't interfere with your running application.

Although each integration test should get its own container instance (if any) you still want to reuse as much of the container configuration code as possible. This can be done by extracting this code to a method that either returns a new configured container instance when called, or configure a supplied container instance (and return nothing). This method should typically do an incomplete configuration and the caller (either your tests or global asax) should add the missing configurations.

Extracting this code: allows you to have multiple end application that partly share the same configuration; allows you to verify the container in an integration test; and allows you to add services that need to be mocked by your integration tests.

To make life easier, Simple Injector allows you to replace existing registrations with new one (for instance a mocked one). You can enable this as follows:

container.Options.AllowOverridingRegistrations = true;

But be careful with this! This option can hide the fact that you accidentally override a registration. In my experience it is in most cases much better to build up an incomplete container and add the missing registrations afterwards instead of overriding them. Or if you decide to override, enable the feature at the last possible moment to prevent any accidental misconfigurations.

like image 101
Steven Avatar answered Oct 24 '22 09:10

Steven