简体   繁体   English

如何在 TestContainers 中自动设置 Entity Framework 的 Code First 数据库以进行测试?

[英]How to automate DB setup of Entity Framework's Code First in TestContainers for testing?

In order to setup the TestContainer 's MsSqlTestcontainer for testing, I copied the script emitted by dotnet ef migrations script and planted it in the unit-test's setup (actual code doesn't:为了设置TestContainerMsSqlTestcontainer进行测试,我复制了dotnet ef migrations script发出的脚本并将其植入单元测试的设置中(实际代码没有:

void PrepareDb(MsSqlTestcontainer dbTestContainer){
    dbTestContainer.ExecScriptAsync(@"CREATE TABLE [DbName] ( /* ... */ )");
}

Is there a way to automate it, for instance if the DB model ever changes, and wire-up eg MyDbContext straight to the TestContainer 's logic?有没有办法使其自动化,例如,如果数据库模型发生变化,并将MyDbContext直接连接到TestContainer的逻辑?

I was considering passing the MyDbContext code into the container and run dotnet ef migrations script inside of it, but I'm not sure how much it worth the effort (and I need to use a container that already has dotnet installed, which is another complication..).我正在考虑将MyDbContext代码传递到容器中并在其中运行dotnet ef migrations script ,但我不确定它值得付出多少努力(而且我需要使用已经安装了dotnet的容器,这是另一个复杂问题..).

That's the dbTestContainer setup, FWIW:这是dbTestContainer设置,FWIW:

var dbTestContainer = new TestcontainersBuilder<MsSqlTestcontainer>()
    .WithDatabase(new MsSqlTestcontainerConfiguration { Password = "whatever secret" })
    .WithImage("mcr.microsoft.com/azure-sql-edge")
    .WithWaitStrategy(Wait.ForUnixContainer())
    .Build()

Connect to the containerized database and run migrations programmatically before tests start with在测试开始之前连接到容器化数据库并以编程方式运行迁移

dbContext.Database.Migrate();

I had the same problem and I managed to solve it, at least for my own basic needs.我遇到了同样的问题,但我设法解决了它,至少是为了满足我自己的基本需求。 In case you still need a solution, here is what I did.如果您仍然需要解决方案,这就是我所做的。

Note: If anyone knows a better approach or spots any flaws below, I would be very happy to hear about it.注意:如果有人知道更好的方法或发现以下任何缺陷,我将非常高兴听到。

I have a IntegrationTestWebApplicationFactory which I use to do the usual configuration for my integration tests.我有一个IntegrationTestWebApplicationFactory ,我用它来为我的集成测试做通常的配置。 As Pavel already pointed out, you can run the migrations programmatically before the tests start.正如 Pavel 已经指出的那样,您可以在测试开始之前以编程方式运行迁移。 For this, my IntegrationTestWebApplicationFactory implements the IAsyncLifetime interface of XUnit , which I am using for testing.为此,我的IntegrationTestWebApplicationFactory实现了我用于测试的XUnitIAsyncLifetime接口。 This interface requires you to implement InitializeAsync and DisposeAsync methods.此接口要求您实现InitializeAsyncDisposeAsync方法。 Inside of InitializeAsync I run the await dbContext.Database.MigrateAsync();InitializeAsync内部,我运行await dbContext.Database.MigrateAsync(); command.命令。

Here is the full code of my IntegrationTestWebApplicationFactory class:这是我的IntegrationTestWebApplicationFactory类的完整代码:

public class IntegrationTestWebApplicationFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
    private readonly TestcontainerDatabase _container;

    public IntegrationTestFactory()
    {
        _container = new TestcontainersBuilder<MsSqlTestcontainer>()
            .WithDatabase(new MsSqlTestcontainerConfiguration
            {
                Username = "sa",
                Database = "WeatherApp",
                Password = "2@LaiNw)PDvs^t>L!Ybt]6H^%h3U>M",
            })
            .WithImage("mcr.microsoft.com/mssql/server:2022-latest")
            .WithCleanUp(true)
            .Build();
    }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            services.AddDbContext<DemoDbContext>(options => { options.UseSqlServer(_container.ConnectionString); });
        });
    }

    public async Task InitializeAsync()
    {
        await _container.StartAsync();
        using var scope = Services.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<DemoDbContext>();
        await dbContext.Database.MigrateAsync();
    }

    public new async Task DisposeAsync() => await _container.DisposeAsync();
}

And this is how I used it in my integration tests:这就是我在集成测试中使用它的方式:

    [Theory]
    [InlineAutoData]
    public async Task GettingWeatherForecastReturnsOkay(WeatherForecast expectedForecast)
    {
        var client = _integrationTestFactory.CreateClient();

        // insert into db what you want to assert
        await client.PostAsJsonAsync("WeatherForecast", expectedForecast);
        
        // read from db
        var forecasts = await client.GetFromJsonAsync<List<WeatherForecast>>("WeatherForecast");

        // do asserts or whatever..
        forecasts.Should().NotBeEmpty();
        forecasts.Should().ContainEquivalentOf(expectedForecast);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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