简体   繁体   中英

ASP.NET Core 2.2 Integration Testing DbContext Service not registered

Setup

  • Windows 10
  • Visual Studio Professional 2017 v15.9.9
  • ASP.NET Core 2.2
  • EF Core 2.2
  • Dapper
  • xUnit 2.4.1

Description

I'm using the WebApplicationFactory from the Microsoft.AspNetCore.Mvc.Testing package to setup my integration tests.

I've been following the official documentation to customise the web host configuration.

The SUT uses Dapper to query from the database, so I'm not using the In-Memory provider that ships with EF Core for this particular integration test.

My code for setting up the WebApplictionFactory is below:

public class CustomWebApplicationFactory<TStartup>
    : WebApplicationFactory<TStartup> where TStartup : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder
            .UseStartup<TStartup>()
            .ConfigureServices(services =>
        {
            var sp = services.BuildServiceProvider();

            // Create a scope to obtain a reference to the database context
            using (var scope = sp.CreateScope())    
            {
                var scopedServices = scope.ServiceProvider;
                var dbContext = scopedServices.GetRequiredService<MyDbContext>(); // <-- service not found

                dbContext.Database.EnsureCreated();

                new MyDbContextSeed()
                    .SeedAsync(dbContext)
                    .Wait();
            }
        });
    }
}

Issue

The MyDbContext service isn't found, I understand why (I think) - because the ServiceProvider from my Startup.cs class hasn't built yet.

But the question is How can I access the services from my Startup class here?

For context, the Integration test looks like this:

public class MyAPITests
    : IClassFixture<CustomWebApplicationFactory<Startup>>
{
    private readonly HttpClient _client;
    private readonly CustomWebApplicationFactory<Startup> _factory;

    public MyAPITests(CustomWebApplicationFactory<Startup> factory)
    {
        _factory = factory;
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task Get_ItemAsync_WhenIdNotFound_ReturnsNotFoundStatusCode()
    {
        // Arrange
        var request = new HttpRequestMessage(HttpMethod.Get, "api/v1/item/0");

        // Act
        var response = await _client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }
}

First, ConfigureServices can be used instead of UseStartup , not together. Second, you shouldn't create the scope and do the migration during ConfigureServices anyways, but rather after the Web Host is built, see here :

In older tutorials, you may see similar code in the Configure method in Startup.cs. We recommend that you use the Configure method only to set up the request pipeline. Application startup code belongs in the Main method.

The only way to do this is not in the factory, but after the factory built it:

public class MyAPITests
    : IClassFixture<CustomWebApplicationFactory<Startup>>
{
    private readonly HttpClient _client;
    private readonly CustomWebApplicationFactory<Startup> _factory;

    public MyAPITests(CustomWebApplicationFactory<Startup> factory)
    {
        _factory = factory;
        _client = factory.CreateClient();

        var host = factory.Server.Host;
        using (var scope = host.Services.CreateScope())
        {
            var scopedServices = scope.ServiceProvider;
            var dbContext = scopedServices.GetRequiredService<MyDbContext>();

            dbContext.Database.EnsureCreated();

            new MyDbContextSeed()
                .SeedAsync(dbContext)
                .Wait();
        }
    }

    //...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM