I’m working on a .NET project where I want to integrate WebApplicationFactory with Testcontainers to use PostgreSQL for my SpecFlow tests. I’m facing some challenges ensuring that the PostgreSQL container is started and ready before the application configuration is applied. I want to ask first if this is feasible in the first place? My biggest challenge is configuring and using the context, it is not working. Can I inject any service I wanr with WebHostFactory? Here’s what I’ve tried so far:
CustomWebApplicationFactory:
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using System.Threading.Tasks;
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup>, IAsyncLifetime where TStartup : class
{
private readonly TestcontainerDatabase _postgresContainer;
public CustomWebApplicationFactory()
{
_postgresContainer = new TestcontainersBuilder<PostgreSqlTestcontainer>()
.WithDatabase(new PostgreSqlTestcontainerConfiguration
{
Database = "testdb",
Username = "postgres",
Password = "password"
})
.Build();
}
public async Task InitializeAsync()
{
await _postgresContainer.StartAsync();
}
public async Task DisposeAsync()
{
await _postgresContainer.StopAsync();
await _postgresContainer.DisposeAsync();
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<MyDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
services.AddDbContext<MyDbContext>(options =>
{
options.UseNpgsql(_postgresContainer.ConnectionString);
});
services.AddScoped<IMyRepository, MyRepository>();
var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<MyDbContext>();
db.Database.EnsureCreated();
}
});
}
public MyDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<MyDbContext>()
.UseNpgsql(_postgresContainer.ConnectionString)
.Options;
return new MyDbContext(options);
}
public IServiceScope CreateScope()
{
return Services.CreateScope();
}
}
SpecFlow Hooks:
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http;
using System.Threading.Tasks;
using TechTalk.SpecFlow;
[Binding]
public class SpecFlowHooks
{
private static CustomWebApplicationFactory<Startup> _factory;
private static HttpClient _client;
private static MyDbContext _dbContext;
private static IServiceScope _scope;
[BeforeTestRun]
public static async Task BeforeTestRun()
{
_factory = new CustomWebApplicationFactory<Startup>();
await _factory.InitializeAsync();
_client = _factory.CreateClient();
_dbContext = _factory.CreateDbContext();
_scope = _factory.CreateScope();
}
[AfterTestRun]
public static async Task AfterTestRun()
{
_client.Dispose();
_dbContext.Dispose();
_scope.Dispose();
await _factory.DisposeAsync();
}
public static HttpClient Client => _client;
public static MyDbContext DbContext => _dbContext;
public static IServiceScope Scope => _scope;
}