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