Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 throughs an exception. It is an extension method I don't know what is the exact class I should mock so that the CreateScope call works.

like image 514
guillem Avatar asked Dec 06 '22 10:12

guillem


1 Answers

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.

like image 79
Scott Hannen Avatar answered Dec 22 '22 00:12

Scott Hannen