DEV Community 👩‍💻👨‍💻

Andreas Jakof
Andreas Jakof

Posted on

IoC-Container with Mocks for Unit Testing

I was looking for an IServiceCollection/IServiceProvider using Mocks instead of the real thing, in one class (or two dependend classes), to test my StartUp-Code for multiple WebSites/WebServices. I recently converted them to IoC, which gave me some headaches at first, because I forgot to register some of the later needed types to the IoC-Container.
And since I did not find any, I decided to build one of my own.
I call it MockProvider and you can use it via NuGet as well.

You can use DI in ASP.NET CORE in multiple ways.

  • Constructor Injection into classes, that are created by the Framework. In my case the controllers.
    public class MyController : Controller
    {
        public MyController(IFoo myFoo){ ... }
    }
  • Method Injection, into methods that are called from ASP.NET CORE. Like Controller Methods using [FromServices] Attribute.
public class MyController : Controller
    {
        public async Task<IActionResult> Index([FromServices]IFoo myFoo){ ... }
    }
  • You could always use it "manually" providing an instance of your IServiceProvider to many constructors and heve the Dependency resolved in the constructor, the method where it is needed. I don't say you should, but you could.

Well, I was interested in the first two, so I created a small Startup Tester as well, to keep my boiler plate code to a minimum ... and because I like a challenge.

What is this about?

This little helper provides you with an implementation for IServiceProvider and IServiceCollection to be used as a replacement for the default ASP.NET CORE types during unit tests and uses Mocks from Moq.

I personally use it to test, whether the startup type registrations to the IoC-Container are complete.

What can I do with it?

You can use it as IServiceCollection and IServiceProvider within StartUp and later for e.g. Controllers/Methods, that make use of .NET CORE's Depedency Injection.

If you don't want to run through the complete setup every time, you may also add Mocks of your own.

Use it as IServiceCollection

Create an empty MockProvider and use it as your IServiceCollection in any default StartUp.ConfigureServices.

Use it as IServiceProvider

Create a filled MockProvider and use it as your IServiceProvider in any method, that wants one.

Add you own Mocks

The easiest way to add specific mocks to it, is to use the CreateMock<T> method.

    var m = new MockProvider();
    m.CreateMock<IFoo>();
    Assert.IsNotNull(m.GetService<IFoo>());

There are some overloads:

  • public Mock<U> CreateMock<U>()
    This is the most basic version. It finds constructor parameters and creates mocks for them as well. When done, it returns your created mock.
    Since this will use previously registered types for constructor parameter matching and directly creates the mock, should only be used after putting the parameter types in the container or for constructor parameterless types.

  • public Mock<U> CreateMock<U>(params object[] o)
    This lets you specify the constructor parameters and returns your created mock.

  • public MockDescriptor CreateMock(Type serviceType)
    Like the first one, this will do everything by itself, but handing back an internal representation of the IoC-Registration. The Mock is created lazily on first use.

  • public MockDescriptor CreateMock(Type serviceType, params object[] o)
    Like the second one, this lets you specify the constructor parameters, but handing back an internal representation of the IoC-Registration. The Mock is created lazily on first use.

  • public MockDescriptor CreateMock(Type serviceType, IEnumerable<object> o)
    Like the second one, this lets you specify the constructor parameters, but handing back an internal representation of the IoC-Registration. The Mock is created lazily on first use.

And then there is:

  • public void Add(ServiceDescriptor item) Using the IServiceCollections internal type, there are three ways to go:
    1. The default ServiceDescriptor from .NET Core's IoC Container. This will use the ServiceType and register a Mock for it. The Mock is created lazily on first use.
    2. There is a MockDescriptor derived from ServiceDescriptor, which allows you to first create your Mock and then add it to the IoC-Container. The MockDescriptor-Instance is added to the Container, as it is.
    3. There is also a InstanceDescriptor, which will add a real object to the Container, allowing you to implement your own stub and use it in the IoC-Container. The InstanceDescriptor-Instance is added to the Container, as it is, since it derives from MockDescriptor.

FAQ

When registering the same service type multiple time, which one will be returned on GetService<T>()?

    var fooMock1 = new Mock<IFoo>();
    var fooMock2 = new Mock<IFoo>();
    var m = new MockProvider();
    m.Add<IFoo>(fooMock1);
    m.Add<IFoo>(fooMock2);

The first entry wins. So the above will add only fooMock1 to the container.

Top comments (0)

19 Valuable Github Repositories for Beginners

>> Check out this classic DEV post <<