Search code examples
c#wpfunit-testingc#-4.0mef

How to use IoC in unit tests properly?


We have a WPF application. Many of the ViewModels use the same dependencies that have to be mocked. Sometimes, constructors of the ViewModels have too many dependencies (over-injection) exposed with the only intention - to allow unit testing. For example:

[ImportingConstructor]
public PasswordInputViewModel(
    IPaymentSystemProvider provider,
    IAppContext appCtx,
    IEventAggregator eventAggregator,
    IPromptCreator promptCreator) {
}

The point is that three of the dependencies are dependencies from the infrastructure. Exposing them just for explicit injection with the only intention to allow unit testing adds more noise than helpful information of the dependencies of the ViewModel. Especially, because almost all VMs have these dependencies.

Because of that, we moved these dependencies into the BaseViewModel class:

public class ScreenExtended : Screen {
    [Import]
    public IEventAggregator eventAggregator { get; private set; }

    [Import]
    public IPromptCreator Prompt { get; private set; }

    [Import]
    public IAppContext CurrentApp { get; private set; }
}

Now, we need to mock them somehow from the unit tests evading constructor injection. So, we can bootstrap the IoC.

And now is the question: how to use IoC properly here? Bootstrap the IoC for every class, or for every test, or make it static and initialize only once? If we make it static than we need somehow recompose the IoC. What would you recommend?


Solution

  • I would say it depends on whether the injected items are concrete implementations or mock objects. If you are using concrete implementations, then you are likely to have state issues between tests if you initialise at the class level.

    I typically reinitialise each of the dependencies per test, even the one concrete class (which mocks a service layer on top of a mock repository). When testing CRUD type functionality, it definitely helps to be able to reset to zero each test, despite the fact that it takes a little longer. Rather have a test that can be run correctly and reliably in isolation or in a list.