Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq IServiceProvider / IServiceScope

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()

like image 757
blgrnboy Avatar asked Jun 02 '17 19:06

blgrnboy


People also ask

What is IServiceProvider .NET core?

The IServiceProvider is responsible for resolving instances of types at runtime, as required by the application. These instances can be injected into other services resolved from the same dependency injection container. The ServiceProvider ensures that resolved services live for the expected lifetime.

What is IServiceScopeFactory?

IServiceScopeFactory is always a Singleton but the IServiceProvider can vary based on the lifetime of the containing class. So if you create a scope and resolve services from this scope, and any of those services take an IServiceProvider, it'll be the scoped one.


2 Answers

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.

like image 142
Nkosi Avatar answered Oct 05 '22 02:10

Nkosi


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.

like image 33
Márton Balassa Avatar answered Oct 05 '22 02:10

Márton Balassa