Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Net Core: Override WebApplicationFactory Services DbContext with InMemory Database

I am customizing WebApplicationFactory to use Startup, appsettings from original Application project.

Purpose is to create Integration test which points to original application startup. The appsettings json is following for dbcontext:

  "ConnectionStrings": {
    "DbConnection": "Data Source=.;Initial Catalog = TestDB; Integrated Security=True"

I want to override Services to use inmemory database, from variable below. How would I conduct this?

Custom Web Application Factory:

namespace Integrationtest
{
    public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureAppConfiguration((hostingContext, configurationBuilder) =>
            {
                var type = typeof(TStartup);
                var path = @"C:\OriginalApplication";

                configurationBuilder.AddJsonFile($"{path}\\appsettings.json", optional: true, reloadOnChange: true);
                configurationBuilder.AddEnvironmentVariables();
            });
        }
    }
}

Actual Integration Test:

public class DepartmentAppServiceTest : IClassFixture<CustomWebApplicationFactory<OriginalApplication.Startup>>

{
    public dbContextTest context;
    public CustomWebApplicationFactory<OriginalApplication.Startup> _factory;
    public DepartmentAppServiceTest(CustomWebApplicationFactory<OriginalApplication.Startup> factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task DepartmentAppTest()
    {
        using (var scope = _factory.Server.Host.Services.CreateScope())
        {
            context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
            context.SaveChanges();

            var foo = scope.ServiceProvider.GetRequiredService<IDepartmentAppService>();
            var departmentDto = await foo.GetDepartmentById(2);
            Assert.Equal("123", departmentDto.DepartmentCode);
        }
    }
}

I want to override the Services database to use inmemory database, from this variable below. How would I conduct this?

           var dbtest = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

           var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: "TestDB")
            .Options;
like image 558
jerrythomas38 Avatar asked Aug 06 '19 23:08

jerrythomas38


1 Answers

You can use WebHostBuilder.ConfigureTestServices to adjust the service configuration that the integration test server uses. That way, you can reconfigure your database context to use a different configuration. This is also covered in the integration testing chapter of the documentation.

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    // …

    builder.ConfigureTestServices(services =>
    {
        // remove the existing context configuration
        var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>));
        if (descriptor != null)
            services.Remove(descriptor);

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseInMemoryDatabase("TestDB"));
    });
}

The configuration passed to ConfigureTestServices will always run after the Startup.ConfigureServices, so you can use this to override the real services for your integration tests.

For most situations, it will be enough to simply register some other type over the existing registration to make it apply everywhere. Unless you actually retrieve multiple services of a single type (by injecting IEnumerable<T> somewhere), this will not have a negative effect.

like image 86
poke Avatar answered Sep 28 '22 18:09

poke