2
votes

I've been creating integration tests using Xunit for a .NET Core 3.1 web app that uses a database. For testing I've swapped to an in-memory database following along from the Microsoft Documentation. The CustomWebApplicationFactory code is:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
        {
        protected override void ConfigureWebHost(IWebHostBuilder webHostBuilder)
            {
            webHostBuilder.ConfigureServices(services =>
                {
                    // Remove the app's database  registration.
                    var serviceDescriptor = services
                                                .SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<MyDbContext>));

                    if (serviceDescriptor != null)
                        {
                        services.Remove(serviceDescriptor);
                        }

                    // Add MyDbContext using an in-memory database for testing.
                    services.AddDbContext<MyDbContext>(options =>
                    {
                        options.UseInMemoryDatabase("InMemoryDbForTesting");
                    });

                    var servicesProvider = services.BuildServiceProvider();

                    // Create a scope to obtain a reference to the database context (MyDbContext).
                    using (var serviceScope = servicesProvider.CreateScope())
                        {
                        var scopedServices = serviceScope.ServiceProvider;
                        var db = scopedServices.GetRequiredService<MyDbContext>();
                        var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

                        db.Database.EnsureCreated();                                                                                                                                         // Ensure the database is created.

                        try
                            {
                            DatabaseSeeding.voidInitialiseCazIdentityProviderDatabase(db);                                                                                                                              // Seed the database with test data.
                            }
                        catch (Exception ex)
                            {
                            logger.LogError(ex, $"An error occurred seeding the database with data. Error: {ex.Message}");
                            }
                        }
                });
            }

My basic page tests work fine with this arrangement, but I now want to check to see if the in-memory database has been corrected modified by the integration test. Whatever is happening, the reference to the database is not ending up in the Xunit DI container (if such a thing exists). My test class is initialised with the following code:

    public class IdpUserServiceTests : IClassFixture<CustomWebApplicationFactory<Startup>>
    {
    private readonly CustomWebApplicationFactory<Startup> _webApplicationFactory;
    private readonly ITestOutputHelper _testOutput;
    private readonly HttpClient _httpClient;
    private readonly MyDbContext _myDbContext;

    public IdpUserServiceTests(CustomWebApplicationFactory<Startup> webApplicationFactory, MyDbContext myDbContext, ITestOutputHelper testOutput)
        {
        _webApplicationFactory = webApplicationFactory;
        _myDbContext = myDbContext;
        _testOutput = testOutput;
        _httpClient = _webApplicationFactory.CreateClient();
        }

    //Tests 

but in attempting to run a test, I get the following error:

The following constructor parameters did not have matching fixture data: MyDbContext objMyDbContext

I'm looking for the correct way to access the in-memory database - it's obviously not via constructor injection. I've had a go with this answer - Access in memory dbcontext in integration test - but things seem to have changed between 2.2 and 3.1.

1

1 Answers

2
votes

It turned out that the Access in memory dbcontext in integration test answer referenced above was not quite right for ASP.NET Core 3.1. The scopeFactory has to be found from a different source:

var scopeFactory = _webApplicationFactory.Services.GetService<IServiceScopeFactory>();

So the test class now looks like:

public class ServiceTests : IClassFixture<CustomWebApplicationFactory<Startup>>
    {
    private readonly CustomWebApplicationFactory<Startup> _webApplicationFactory;
    private readonly ITestOutputHelper _testOutput;
    private readonly HttpClient _httpClient;

    public ServiceTests(CustomWebApplicationFactory<Startup> webApplicationFactory, ITestOutputHelper testOutput)
        {
        _webApplicationFactory = webApplicationFactory;
        _testOutput = testOutput;
        _httpClient = _webApplicationFactory.CreateClient();
        }

    [Fact]

    public async Task AServiceTest()
        {
        // Do some HTTP client stuff that alters the in-memory database

      var scopeFactory = _webApplicationFactory.Services.GetService<IServiceScopeFactory>();

        using(var scope = scopeFactory.CreateScope())
            {
            var myDataContext = scope.ServiceProvider.GetService<MyDataContext>();

            // Query the in-memory database
            }
        }
    }