I am trying to create a Mock (using Moq) for an IServiceProvider
so that I can test my repository class:
public class ApiResourceRepository : IApiRe
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>();
}
}
}
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);
}
}
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.
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.
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.