I am writing an integration test for my ASP.NET Core MVC application. The test is about to send a POST
request to controller and then check if database was updated correctly.
I have a CustomWebApplicationFactory
where I am trying to configure SQLite in-memory database, but probably I do something wrong.
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
private SqliteConnection Connection;
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
Connection = new SqliteConnection("DataSource=:memory:");
Connection.Open();
builder.UseEnvironment("Development");
builder.ConfigureTestServices(services =>
{
// Unregister existing database service (SQL Server).
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<AppDbContext>));
if (descriptor != null) services.Remove(descriptor);
// Register new database service (SQLite In-Memory)
services.AddDbContext<AppDbContext>(options => options.UseSqlite(Connection));
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Connection.Close();
}
}
My test looks like this:
public class OrderControllerTests : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _httpClient;
private readonly AppDbContext _context;
public OrderControllerTests(CustomWebApplicationFactory<Startup> factory)
{
_httpClient = factory.CreateDefaultClient();
var scopeFactory = factory.Services.GetService<IServiceScopeFactory>();
using var scope = scopeFactory.CreateScope();
_context = scope.ServiceProvider.GetService<AppDbContext>();
}
[Fact]
public async Task Create_Post_OrderIsCreated()
{
// ...
_context.Customers.Add(customer);
_context.SaveChanges();
// ...
}
}
When I run the test, the line _context.Customers.Add(customer);
triggers the CustomWebApplicationFactory.Dispose()
method and I get an error:
System.ObjectDisposedException : Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'AppDbContext'.
The error message is very descriptive, but I don't know how to solve it. Why is the database context getting disposed?
I think I've figured it out, the issue is that _context
is available only in the _scope
, so I removed using
and now I have shared database between class tests. Each test have also the option to empty and populate the database.
private readonly HttpClient _httpClient;
private readonly AppDbContext _context;
private readonly IServiceScope _scope;
public OrderControllerTests(CustomWebApplicationFactory<Startup> factory)
{
_httpClient = factory.CreateDefaultClient();
_scope = (factory.Services.GetRequiredService<IServiceScopeFactory>()).CreateScope();
_context = _scope.ServiceProvider.GetRequiredService<AppDbContext>();
// database is now shared across tests
_context.Database.EnsureCreated();
}