I've set up a project based on https://github.com/jasontaylordev/CleanArchitecture. But I'm having some troubles with writing my integration tests for the controllers, since the in-memory database does not reset between each test.Each test use a WebApplicationFactory in order to set up a test web server,
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Remove the app's ApplicationDbContext registration.
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<ApplicationDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Add a database context using an in-memory
// database for testing.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
// Register test services
services.AddScoped<ICurrentUserService, TestCurrentUserService>();
services.AddScoped<IDateTime, TestDateTimeService>();
services.AddScoped<IIdentityService, TestIdentityService>();
// Build the service provider
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database
// context (ApplicationDbContext).
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var context = scopedServices.GetRequiredService<ApplicationDbContext>();
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
// Ensure the database is created.
context.Database.EnsureCreated();
try
{
// Seed the database with test data.
SeedSampleData(context);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {Message}", ex.Message);
}
}
})
.UseEnvironment("Test");
}
...
Where the following create test:
namespace CleanArchitecture.WebUI.IntegrationTests.Controllers.TodoItems
{
public class Create : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly CustomWebApplicationFactory<Startup> _factory;
public Create(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task GivenValidCreateTodoItemCommand_ReturnsSuccessCode()
{
var client = await _factory.GetAuthenticatedClientAsync();
var command = new CreateTodoItemCommand
{
Title = "Do yet another thing."
};
var content = IntegrationTestHelper.GetRequestContent(command);
var response = await client.PostAsync($"/api/todoitems", content);
response.EnsureSuccessStatusCode();
}
...
And the following read test:
namespace CleanArchitecture.WebUI.IntegrationTests.Controllers.TodoItems
{
public class Read: IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly CustomWebApplicationFactory<Startup> _factory;
public Read(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task ShouldRetriveAllTodos()
{
var client = await _factory.GetAuthenticatedClientAsync();
var response = await client.GetAsync($"/api/todoitems");
var todos = Deserialize(response);
todos.Count().Should().Be(1); //but is 2 because database is shared between read and create test class.
}
The problem is that the in-memory database is not reset between each test. I've tried generating a different name for the in-memory database using Guid.New.ToString
, but then the tests don't find the seeded database data, and by putting the tests in the same XUnit collection, to no avail.
Any good ideas how to make the tests not share database?
What worked for me was to generate a DBName for each WebApplicationFactory
instance, and I instantiate one of them for each test. So the test looks like this:
[Fact]
public void Test()
{
// Arrange
var appFactory = new WebApplicationFactory();
// ...
// Act
// ...
// Assert
// ...
}
And the WebApplicationFactory
:
public class TestWebApplicationFactory : WebApplicationFactory<Startup>
{
private readonly string _dbName = Guid.NewGuid().ToString();
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureServices(services =>
{
services.AddDbContext<DbContext>(options =>
{
// This is what makes a unique in-memory database per instance of TestWebApplicationFactory
options.UseInMemoryDatabase(_dbName);
});
});
}
}
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