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 :
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 e.g. MyDbContext straight to the Testcontainer's logic?
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..).
That's the dbTestContainer setup, FWIW:
var dbTestContainer = new TestcontainersBuilder<MsSqlTestcontainer>()
.WithDatabase(new MsSqlTestcontainerConfiguration { Password = "whatever secret" })
.WithImage("mcr.microsoft.com/azure-sql-edge")
.WithWaitStrategy(Wait.ForUnixContainer())
.Build()
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. As Pavel already pointed out, you can run the migrations programmatically before the tests start. For this, my IntegrationTestWebApplicationFactoryimplements the IAsyncLifetime interface of XUnit, which I am using for testing. This interface requires you to implement InitializeAsync and DisposeAsync methods. Inside of InitializeAsync I run the await dbContext.Database.MigrateAsync(); command.
Here is the full code of my IntegrationTestWebApplicationFactory class:
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With