guillem
guillem

Reputation: 2998

Mock ServiceProvider GetServices

I'm having hard times to test a Factory that uses the .net core ServiceProvider to return a particular implementation given some logic.

using (var scope = _serviceProvider.CreateScope())
{
    var services = scope.ServiceProvider.GetServices<IUrlProcessor>();
}

I'm partially have this with

var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(m => m.GetService(typeof(IEnumerable<IUrlProcessor>)))
    .Returns(new List<IUrlProcessor>() {
        new PassthruProcessor()
    });

The GetServices seems to work but the CreateScope call just throws an exception. It is an extension method and I don't know what is the exact class I should mock so that the CreateScope call works.

Upvotes: 9

Views: 17298

Answers (1)

Scott Hannen
Scott Hannen

Reputation: 29302

In this scenario it probably doesn't help to create mocks that return more mocks. Your class depends on an IServiceProvider, and you need to call CreateScope().

Mocking IServiceProvider to return another mock is functionally the same as using a real ServiceProvider and configuring it to return a mock. The difference is that if you use a real ServiceProvider you don't also have to mock CreateScope.

(I'm totally sidestepping questions about where and when to depend on the IServiceProvider.)

Here's a drastically simplified example:

The class that depends on an IServiceProvider:

public class ScopedFooFactory : IFooFactory
{
    private readonly IServiceProvider _serviceProvider;

    public ScopedFooFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IFoo CreateFoo()
    {
        using (var scope = _serviceProvider.CreateScope())
        {
            return scope.ServiceProvider.GetService<IFoo>();
        }
    }
}

...and some unit test code:

var serviceCollection = new ServiceCollection();
var fooMock = new Mock<IFoo>();
serviceCollection.AddScoped<IFoo>(provider => fooMock.Object);
var serviceProvider = serviceCollection.BuildServiceProvider();
var subject = new ScopedFooFactory(serviceProvider);
var foo = subject.CreateFoo();
Assert.AreSame(fooMock.Object, foo);

To me this is simpler and easier than creating a mock that returns more mocks.

Upvotes: 32

Related Questions