Moq IServiceProvider / IServiceScope

C#Unit Testingasp.net Core.Net CoreMoq

C# Problem Overview


I am trying to create a Mock (using Moq) for an IServiceProvider so that I can test my repository class:

public class ApiResourceRepository : IApiResourceRepository
{
	private readonly IServiceProvider _serviceProvider;

	public ApiResourceRepository(IServiceProvider serviceProvider)
	{
		_serviceProvider = serviceProvider;
		_dbSettings = dbSettings;
	}

	public async Task<ApiResource> Get(int id)
	{
		ApiResource result;

		using (var serviceScope = _serviceProvider.
			GetRequiredService<IServiceScopeFactory>().CreateScope())
		{
			var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
			result = await
				context.ApiResources
				.Include(x => x.Scopes)
				.Include(x => x.UserClaims)
				.FirstOrDefaultAsync(x => x.Id == id);
		}

		return result;
	}
}

My attempt at creating the Mock object is as follows:

Mock<IServiceProvider> serviceProvider = new Mock<IServiceProvider>();

serviceProvider.Setup(x => x.GetRequiredService<ConfigurationDbContext>())
	.Returns(new ConfigurationDbContext(Options, StoreOptions));

Mock<IServiceScope> serviceScope = new Mock<IServiceScope>();

serviceScope.Setup(x => x.ServiceProvider).Returns(serviceProvider.Object);

serviceProvider.Setup(x => x.CreateScope()).Returns(serviceScope.Object);

I am receiving the following error:

> System.NotSupportedException : Expression references a method that > does not belong to the mocked object: x => > x.GetRequiredService()

C# Solutions


Solution 1 - C#

As already stated, Moq does not allow setup of extension methods.

In this case however the source code of the said extension methods are available on Github

ServiceProviderServiceExtensions.

The usual way around an issue like this is to find out what the extension methods do and mock a path safely through it's execution.

The base type in all of this is the IServiceProvider and its object Getservice(Type type) method. This method is what is ultimately called when resolving the service type. And we are only dealing with abstraction (interfaces) then that makes using moq all the more easier.

//Arrange
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider
    .Setup(x => x.GetService(typeof(ConfigurationDbContext)))
    .Returns(new ConfigurationDbContext(Options, StoreOptions));

var serviceScope = new Mock<IServiceScope>();
serviceScope.Setup(x => x.ServiceProvider).Returns(serviceProvider.Object);

var serviceScopeFactory = new Mock<IServiceScopeFactory>();
serviceScopeFactory
    .Setup(x => x.CreateScope())
    .Returns(serviceScope.Object);

serviceProvider
    .Setup(x => x.GetService(typeof(IServiceScopeFactory)))
    .Returns(serviceScopeFactory.Object);

var sut = new ApiResourceRepository(serviceProvider.Object);

//Act
var actual = sut.Get(myIntValue);

//Asssert
//...

Review the code above and you would see how the arrangement satisfies the expected behavior of the extension methods and by extension (no pun intended) the method under test.

Solution 2 - C#

The general rule is that you don't mock types that you don't own. Unless you need to verify the calls made to the service provider, just build the IServiceProvider from a ServiceCollection in your tests.

Solution 3 - C#

I'd like to argue that when you need to add that much ceremony just to mock a simple method, then maybe your code isn't very testable. So another option would be to hide the service locator behind a more test and mock friendly interface (and in my opinion a nicer one too):

public interface IServiceLocator : IDisposable
{
	T Get<T>();
}

public class ScopedServiceLocator : IServiceLocator
{
	private readonly IServiceScopeFactory _factory;
	private IServiceScope _scope;

	public ScopedServiceLocator(IServiceScopeFactory factory)
	{
		_factory = factory;
	}

	public T Get<T>()
	{
		if (_scope == null)
			_scope = _factory.CreateScope();

		return _scope.ServiceProvider.GetService<T>();
	}


	public void Dispose()
	{
		_scope?.Dispose();
		_scope = null;
	}
}

I've only implemented the GetService<T> method here, but you could easily add/remove so that the locator better suites your need. And an example of how to use it;

public class ALongRunningTask : IRunForALongTime
{
	private readonly IServiceLocator _serviceLocator;

	public ALongRunningTask(IServiceLocator serviceLocator)
	{
		_serviceLocator = serviceLocator;
	}

	public void Run()
	{
		using (_serviceLocator)
		{
			var repository = _serviceLocator.Get<IRepository>();
		}
	}
}

Solution 4 - C#

Just in case it's useful to somebody, here's an example of how I created my own ServiceProvider for my unit test as suggested here. I also added ServiceScope and ServiceScopeFactory mocks to serve it all up.

Here's the code in my unit test:

var serviceCollection = new ServiceCollection();

// Add any DI stuff here:
serviceCollection.AddSingleton<ILogger>(loggerMock.Object);

// Create the ServiceProvider
var serviceProvider = serviceCollection.BuildServiceProvider();

// serviceScopeMock will contain my ServiceProvider
var serviceScopeMock = new Mock<IServiceScope>();
serviceScopeMock.SetupGet<IServiceProvider>(s => s.ServiceProvider)
    .Returns(serviceProvider);

// serviceScopeFactoryMock will contain my serviceScopeMock
var serviceScopeFactoryMock = new Mock<IServiceScopeFactory>();
serviceScopeFactoryMock.Setup(s => s.CreateScope())
    .Returns(serviceScopeMock.Object);
	

I can then pass my serviceScopeFactoryMock to my sut constructor.

Here's the code that's being tested:

using (var scope = _serviceScopeFactory.CreateScope())
{
	var logger = scope.ServiceProvider.GetRequiredService<ILogger>();
	...
}

Solution 5 - C#

I was also looking for this, but i only needed to mock GetService. I always use AutoFac to auto generate mocks. In this example 'GetService' always returns a mocked instance. You can change the mock behavior afterwards with the freeze method.

Example:

Class to test:

public class ApiResourceRepository : ApiResourceRepository {
            private readonly IServiceProvider _serviceProvider;

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

            public object Get(int id) {
                using (var serviceScope = _serviceProvider.CreateScope()) {
                    var repo = serviceScope.ServiceProvider.GetService<IPersonRepository>();
                    return repo.GetById(id);
                }
            }
        }

Unit Test:

 [Fact]
        public void Test() {
            // arrange
            var fixture = new Fixture()
             .Customize(new AutoMoqCustomization())
             .Customize(new ServiceProviderCustomization());

            fixture.Freeze<Mock<IPersonRepository>>()
                .Setup(m => m.GetById(It.IsAny<int>()))
                .Returns(new Person(Name = "John"));

            // Act
            var apiResource = _fixture.Create<ApiResourceRepository>();
            var person = apiResource.Get(1);

            // Assert
            ...
        }

Custom AutoFac provider

public class ServiceProviderCustomization : ICustomization {

        public void Customize(IFixture fixture) {
            var serviceProviderMock = fixture.Freeze<Mock<IServiceProvider>>();

            // GetService
            serviceProviderMock
               .Setup(m => m.GetService(It.IsAny<Type>()))
               .Returns((Type type) => {
                   var mockType = typeof(Mock<>).MakeGenericType(type);
                   var mock = fixture.Create(mockType, new SpecimenContext(fixture)) as Mock;

                   // Inject mock again, so the behavior can be changed with _fixture.Freeze()
                   MethodInfo method = typeof(FixtureRegistrar).GetMethod("Inject");
                   MethodInfo genericMethod = method.MakeGenericMethod(mockType);
                   genericMethod.Invoke(null, new object[] { fixture, mock });

                   return mock.Object;
               });

            // Scoped
            var serviceScopeMock = fixture.Freeze<Mock<IServiceScope>>();
            serviceProviderMock
               .As<IServiceScopeFactory>()
               .Setup(m => m.CreateScope())
               .Returns(serviceScopeMock.Object);

            serviceProviderMock.As<ISupportRequiredService>()
                .Setup(m => m.GetRequiredService(typeof(IServiceScopeFactory)))
                .Returns(serviceProviderMock.Object);
        }
    }

Solution 6 - C#

DISCLAIMER: The embedded links point to subpages of my GitHub and NuGet Page. But I hope it helps you or someone else, never the less.


I just created exaclty such a thing because I couldn't find any. It implements IServiceCollection and IServiceProvider to test my Startup-Configuration, especially, whether all types are registered properly to the DI-Container. And it is a general purpose replacement for those interfaces, providing Mocks (Moq) as singletons for each Registered Type. Foo<Bar> is different from Foo<Bus>.

There is a readme.md on GitHub and the code base is not that big.

There is also a nuget package called MockProvider and - as mentioned - the code is on GitHub. I put it under MIT, so do what you want with it. It is free to use and contribute.

Consider it a way of giving back.

Solution 7 - C#

Here is my snippet for mocking inside scoped service provider. Useful for testing IHostedService etc:

Mock<IServiceProvider> CreateScopedServicesProvider(params (Type @interface, Object service)[] services)
{
	var scopedServiceProvider = new Mock<IServiceProvider>();

	foreach (var (@interfcae, service) in services)
	{
		scopedServiceProvider
			.Setup(s => s.GetService(@interfcae))
			.Returns(service);
	}

	var scope = new Mock<IServiceScope>();
	scope
		.SetupGet(s => s.ServiceProvider)
		.Returns(scopedServiceProvider.Object);

	var serviceScopeFactory = new Mock<IServiceScopeFactory>();
	serviceScopeFactory
		.Setup(x => x.CreateScope())
		.Returns(scope.Object);

	var serviceProvider = new Mock<IServiceProvider>();
	serviceProvider
		.Setup(s => s.GetService(typeof(IServiceScopeFactory)))
		.Returns(serviceScopeFactory.Object);

	return serviceProvider;
}

Usage:

var service = new Mock<IMyService>();
var serviceProvider = CreateScopedServicesProvider((typeof(IMyService), scopedService.Object));
var sut = new ServiceThatUsesScopes(serviceProvider.Object)

Solution 8 - C#

I use Moq and xUnit for my testing. I've ran into a similar issue before and my solution was to extract data transactions into a SqlExecuter class with an interface so I can directly mock the responses from the database. That simplified everything enough to be able to just build the service provider and pass that in. You'll need xUnit, Moq, and some Microsoft packages (Microsoft.EntityFrameworkCore & Microsoft.EntityFrameworkCore.InMemory).

SqlExecuter.cs

public interface ISqlExecuter
{
	Task<List<SqlParameter>> FirstOrDefaultApiResource(ConfigurationDbContext context, int id);
}

public class SqlExecuter : ISqlExecuter
{
	public async Task<ApiResource> FirstOrDefaultApiResource(ConfigurationDbContext context, int id) =>
		return await context.ApiResources
			.Include(x => x.Scopes)
			.Include(x => x.UserClaims)
			.FirstOrDefaultAsync(x => x.Id == id);
} 

ApiResourceRepository.cs

public class ApiResourceRepository : IApiResourceRepository
{
    private readonly IServiceProvider _serviceProvider;
	private readonly ISqlExecuter _sqlExecuter;

    public ApiResourceRepository(IServiceProvider serviceProvider, ISqlExecuter sqlExecuter)
    {
        _serviceProvider = serviceProvider;
		_sqlExecuter = sqlExecuter;
        _dbSettings = dbSettings;
    }

    public async Task<ApiResource> Get(int id)
    {
        ApiResource result;

        using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
            result = await _sqlExecuter.FirstOrDefaultApiResource(context, id);
        }

        return result;
    }
}

ApiResourceRepositoryTests.cs

[Fact]
public async Task Get_Success()
{
	// Arrange
	var id = 42069;
	var scope = "Scope";
	var claim = "UserClaims";
	var services = new ServiceCollection();
	services.AddDbContext<ConfigurationDbContext>(opt => opt
			.UseInMemoryDatabase(databaseName: $"ConfigurationDbContext-{ DateTime.Now.ToString() }")
			.ConfigureWarnings(x => x.Ignore(InMemoryEventId.TransactionIgnoredWarning)),
		ServiceLifetime.Singleton, ServiceLifetime.Singleton);
	var serviceProvider = services.BuildServiceProvider();
	var mockSqlExecuter = new Mock<SqlExecuter>();
	mockSqlExecuter.Setup(x => x.FirstOrDefaultApiResource(It.IsAny<ConfigurationDbContext>(), It.IsAny<int>()))
		.Returns(new ApiResource() { Id = id , Scope = scope, UserClaims = claim })
	var mockApiResourceRepository = new Mock<ApiResourceRepository>(serviceProvider, mockSqlExecuter.Object);

	// Act
	var result = await mockApiResourceRepository.Object.Get(id);

	
	// Assert
	Assert.NotNull(response);
	Assert.Equal(id, result.Id);
	Assert.Equal(scope, result.Scope);
	Assert.Equal(claim, result.UserClaims);
}

Alternatively instead of using the SqlExecuter class, in other cases I have seeded the context that is set in the service provider.

// Arrange
var id = 42069;
var scope = "Scope";
var claim = "UserClaims";
var services = new ServiceCollection();
services.AddDbContext<ConfigurationDbContext>(opt => opt
		.UseInMemoryDatabase(databaseName: $"ConfigurationDbContext-{DateTime.Now.ToString()}")
		.ConfigureWarnings(x => x.Ignore(InMemoryEventId.TransactionIgnoredWarning)),
	ServiceLifetime.Singleton, ServiceLifetime.Singleton);
var serviceProvider = services.BuildServiceProvider();
var context = Interfaces.ServiceProvider.GetService<ComQueMDSContext>();
context.ApiResources.RemoveRange(context.ApiResources);
context.ApiResources.AddRange(new List<ApiResource>(){ new ApiResource(){ Id = id, Scope = scope, UserClaims = claim } });
context.SaveChanges();
var mockApiResourceRepository = new Mock<ApiResourceRepository>(serviceProvider);

I have also extracted much of this work to a Fixture class and collection, as suggested by xUnit to centralize contexts & reduce test time.

https://xunit.net/docs/shared-context

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionblgrnboyView Question on Stackoverflow
Solution 1 - C#NkosiView Answer on Stackoverflow
Solution 2 - C#Márton BalassaView Answer on Stackoverflow
Solution 3 - C#Kjetil KlaussenView Answer on Stackoverflow
Solution 4 - C#mcmcmcView Answer on Stackoverflow
Solution 5 - C#Michael VonckView Answer on Stackoverflow
Solution 6 - C#AndreasView Answer on Stackoverflow
Solution 7 - C#dimaaanView Answer on Stackoverflow
Solution 8 - C#DMendozaView Answer on Stackoverflow