【问题标题】:Resolving DbContext on a Test project returns a DbContext with a wrong connectionString在测试项目上解析 DbContext 会返回带有错误连接字符串的 DbContext
【发布时间】:2020-10-22 16:35:50
【问题描述】:

我有一个使用通用工厂类的IClassFixture 的测试项目。 例如

public class WeatherForecastAcceptanceTest
    : IClassFixture<WebApi1ApplicationFactory<Startup>>, IDisposable
{
    .....
}

Startup 类执行ConfigureServicesConfigure 方法后,它会执行ConfigureWebHost,我在其中删除原始DbContext 并添加一个在内存中运行的新DbContext。

public class WebApi1ApplicationFactory<TStartup>
    : WebApplicationFactory<TStartup> where TStartup : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            //remove the injected DbContext and inject in-memory
            services.RemoveAll(typeof(DbContext));
            var connection = new SqliteConnection("Data Source=:memory:");
            services.AddDbContext<WebApi1DbContext>(
                options => options.UseSqlite(connection));

            var sp = services.BuildServiceProvider();
            using (var scope = sp.CreateScope())
            {
                //ERROR HERE WITH THE RESOLVED DbContext
                using (var dbContext = scope.ServiceProvider
                    .GetRequiredService<WebApi1DbContext>())
                {
                    try
                    {
                        dbContext.Database.OpenConnection();
                        dbContext.Database.EnsureCreated();
                    }
                    catch (Exception ex)
                    {
                        throw;
                    }
                }
            }
        });
    }
}

我正在解析的 DbContext 具有原始的 connectionString 而不是 InMemory,因此,我所有的测试都是在我的原始数据库中插入内容。

这是我如何使用我的WebApplicationFactory

public class WeatherForecastAcceptanceTest : IClassFixture<WebApi1ApplicationFactory<Startup>>, IDisposable
    {
        private WebApi1ApplicationFactory<Startup> _factory;
        private HttpClient _client;

        public WeatherForecastAcceptanceTest(WebApi1ApplicationFactory<Startup> factory)
        {
            _factory = factory;
            _client = factory.CreateClient();
        }

        [Fact]
        public async Task GetAll_ReturnsElements_WhenPostWasExecutedSucessfully()
        {
            // Arrange
        var weatherForecastForCreationDto = CreateRandomWeatherForecastForCreationDto();

        var content = new JsonContent(weatherForecastForCreationDto);
        //setting the content-type header
        content.Headers.ContentType = new MediaTypeWithQualityHeaderValue(HttpMediaTypes.WeatherForecastType1);

        //create an object
        using (var client = _factory.CreateClient())
        {
            //act
            var responsePost = await _client.PostAsync(ApiRoutes.WeatherForecast.CreateWeatherForecast, content);
            //assert
            responsePost.StatusCode.Should().Be(StatusCodes.Status201Created);
        }

        //check that the object was inserted
        using (var client = _factory.CreateClient())
        {
            _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(HttpMediaTypes.WeatherForecastType1));
            // Act
            var response = await _client.GetAsync(ApiRoutes.WeatherForecast.GetWeatherForecast);

            // Assert
            response.StatusCode.Should().Be(StatusCodes.Status200OK);
            var returnedGet = await response.Content.ReadAsAsync<WeatherForecastDto[]>();
            returnedGet.Should().Contain(dto => dto.Summary == weatherForecastForCreationDto.Summary);
        }
        }

        public void Dispose()
        {
            _client?.Dispose();
            _factory?.Dispose();
        }
    }

如何解决注入的内存中 DbContext?

【问题讨论】:

  • 根据您发布的代码,我看不出解析WebApi1DbContext 不会获得最后注册的WebApi1DbContext 的原因。这意味着发布的代码中不存在某些正在发生的事情。尝试构建MRE 并使用该代码更新问题。如果没有这些信息,恐怕您的问题会在没有得到满意答案的情况下结束。
  • 我会尝试用代码发布一个github repo
  • 嗨 Zinov,请不要那样做。 Stack Overflow 问题必须独立存在。这意味着 all 相关代码必须是您帖子的一部分。为确保您发布重现问题的最少量代码,请尝试创建一个演示问题的控制台应用程序。
  • @Steven,这无法在控制台应用程序上复制,您至少需要 2 个项目,一个用于您的 API,另一个用于测试。我认为上面的代码足以复制问题,知道.net核心上的TestServer和IClassFixture的人应该毫无问题地复制它。我将用更多细节更新我的问题,以提供更多背景信息。谢谢

标签: c# unit-testing dependency-injection entity-framework-core testserver


【解决方案1】:

在处理这个问题一段时间后,根据文档,这是他们的建议

  1. 在启动时读取测试环境变量,就像您在生产或开发中所做的那样,并为此注入正确的 DbContext。

例如,使用 Sqlite 注入内存 DBContext。

    if (_env.IsEnvironment("Test"))
    {
         var connection = new SqliteConnection(connectionDb);
         services.AddDbContextPool<WebApi1DbContext>(options => options.UseSqlite(connection));
    }
  1. 与其尝试以这种方式删除 DbContext,不如尝试将环境变量设置为 WebApplicationFactory,因为 AddDbContext 对容器设置了更多的依赖项,而 RemoveAll 没有考虑。
public class WebApi1ApplicationFactory<TStartup>
        : WebApplicationFactory<TStartup> where TStartup : class
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.UseEnvironment("Test");

            builder.ConfigureServices(services =>
            {
                //DbContext in-memory is injected taking in consideration the Test environment.
                var sp = services.BuildServiceProvider();

                using (var scope = sp.CreateScope())
                {
                    using (var dbContext = scope.ServiceProvider.GetRequiredService<WebApi1DbContext>())
                    {
                        try
                        {
                            dbContext.Database.OpenConnection();
                            dbContext.Database.EnsureCreated();
                        }
                        catch (Exception ex)
                        {
                            //Log errors or do anything you think it's needed
                            throw;
                        }
                    }
                }
            });
        }
    }

此操作将允许您使用 DbContext 检索测试环境所需的 DbContext。

【讨论】:

    猜你喜欢
    • 2021-08-02
    • 1970-01-01
    • 2021-03-09
    • 2014-02-05
    • 2021-10-08
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    相关资源
    最近更新 更多