I've been following the strategies for setting up tests for an ASP.NET Core 2.2 API using the Microsoft documentation at Integration tests in ASP.NET Core.
To summarize, we extend and customize WebApplicationFactory
and use a IWebHostBuilder
to setup and configure various services to provide us with a database context using an in-memory database for testing like below (copied and pasted from the article):
public class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup: class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context (ApplicationDbContext) using an in-memory
// database for testing.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
options.UseInternalServiceProvider(serviceProvider);
});
// 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 db = scopedServices.GetRequiredService<ApplicationDbContext>();
var logger = scopedServices
.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
// Ensure the database is created.
db.Database.EnsureCreated();
try
{
// Seed the database with test data.
Utilities.InitializeDbForTests(db);
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred seeding the " +
"database with test messages. Error: {ex.Message}");
}
}
});
}
}
In the tests we can use the factory and create a client like so:
public class IndexPageTests :
IClassFixture<CustomWebApplicationFactory<RazorPagesProject.Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<RazorPagesProject.Startup>
_factory;
public IndexPageTests(
CustomWebApplicationFactory<RazorPagesProject.Startup> factory)
{
_factory = factory;
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
[Fact]
public async Task Test1()
{
var response = await _client.GetAsync("/api/someendpoint");
}
}
This works fine, but note the call to InitializeDbForTests
which sets up some test data for all tests when the services are configured.
I'd like a reasonable strategy for starting every API test with a clean slate, so that tests don't become dependent on each other. I've been looking for various ways to get a hold of ApplicationDbContext
in my test methods to no avail.
Would it be reasonable to do integration tests in complete isolation from each other, and how could I approach it using ASP.NET Core / EF Core / xUnit.NET?
Ironically, you're looking for EnsureDeleted
instead of EnsureCreated
. That will dump the database. Since the in-memory "database" is schemaless, you don't actually need to ensure it is created or even migrate it.
Additionally, you should not be using a hard-coded name for the in-memory database. That will actually cause the same database instance in-memory to be used everywhere. Instead, you should use something random: Guid.NewGuid().ToString()
is good enough.
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