MSTest TestMethod Dependency Injection

2020-07-11 05:36发布

问题:

I'm using a DI container and I want to do MSTest (VS 2010) unit tests with instances resolved from the container.

I'd like to get these instances injected into my TestMethod or at least my TestClass. Is this possible?

Right now my TestMethods directly call container.Resolve<T>(xxx) which I'd prefer to avoid so that my injection testing is more realistic.

Anyone have experience with this?

Thanks in advance.

回答1:

The instantiation of the test class happens deeply in the internal classes of the MSTest framework so injecting dependencies into it would be a challenging task.



回答2:

The main reason -for me- for writing code according to the Dependency Injection pattern and using IoC frameworks is to get testable code. Using a IoC container in your test code however, will get the opposite result. From your question I can see that you are already experiencing this.

This is especially the problem when using the Service Locator (SL) pattern instead of the Dependency Injection (DI) pattern. With the SL pattern, a class calls the IoC container (or an abstraction of such a container) directly, instead of supplying the class with the dependencies it needs (using constructor injection). Because classes are calling the container directly, you need to configure that container in your test environment as well. This is painful because the test configuration or fake objects is often gets very complex, because you often want to influence the behavior of a fake on a per test basis, while keeping things thread-safe, because testing frameworks might run your tests in parallel (MSTest does this). I know I wrote some crazy thread-safe test code in the past, before I found out that I was doing it all wrong :-(.

So you should use the DI pattern in your application code and in your tests you should hook up those dependencies manually. For instance, when having tests for a HomeController class that depends on a ICustomerService class, you should typically have a CreateController() or CreateValidController factory method in your test class that centralizes the creation of that HomeController. This saves you from writing those dependencies in every test and thus creating a maintenance nightmare in your test code. In this factory method you inject for instance a FakeCustomerService class manually, by doing something like this:

private static HomeController CreateController(
    InMemoryDataMapper mapper)
{
    var uowFactory = new FakeNorthwindUnitOfWorkFactory()
    {
        UnitOfWork = new NorthwindUnitOfWork(mapper);
    };

    return new HomeController(new FakeCustomerService(uowFactory));
}

How such a factory method looks like of course depends (no pun intended) on how the dependency structure of the HomeController looks like.

Long story short, do not try to do dependency injection in your test code the same way as you want to do in your application code. Not only do test frameworks make it very hard to do this, it would mean you have to configure your IoC framework for your test environment, in which case you will be heading to failure. Unfortunately, I talk from experience.