I am trying use InMemory EF7 database for my xunit repository test.
But my problem is that when i try to Dispose the created context the in memory db persist. It me
I think the answer Nate gave may be out of date now or maybe I am doing something wrong. UseInMemoryDatabase() now requires a db name.
Below is what I ended up with. I added a line to create a unique db name. I removed the using statements in favor of using the constructor and dispose that are called once for each test case.
There are some debug lines in there from my testing.
public class DeviceRepositoryTests : IClassFixture<DatabaseFixture>, IDisposable
{
private readonly DeviceDbContext _dbContext;
private readonly DeviceRepository _repository;
private readonly ITestOutputHelper _output;
DatabaseFixture _dbFixture;
public DeviceRepositoryTests(DatabaseFixture dbFixture, ITestOutputHelper output)
{
this._dbFixture = dbFixture;
this._output = output;
var dbOptBuilder = GetDbOptionsBuilder();
this._dbContext = new DeviceDbContext(dbOptBuilder.Options);
this._repository = new DeviceRepository(_dbContext);
DeviceDbContextSeed.EnsureSeedDataForContext(_dbContext);
//_output.WriteLine($"Database: {_dbContext.Database.GetDbConnection().Database}\n" +
_output.WriteLine($"" +
$"Locations: {_dbContext.Locations.Count()} \n" +
$"Devices: {_dbContext.Devices.Count()} \n" +
$"Device Types: {_dbContext.DeviceTypes.Count()} \n\n");
//_output.WriteLine(deviceDbContextToString(_dbContext));
}
public void Dispose()
{
_output.WriteLine($"" +
$"Locations: {_dbContext.Locations.Count()} \n" +
$"Devices: {_dbContext.Devices.Count()} \n" +
$"Device Types: {_dbContext.DeviceTypes.Count()} \n\n");
_dbContext.Dispose();
}
private static DbContextOptionsBuilder<DeviceDbContext> GetDbOptionsBuilder()
{
// The key to keeping the databases unique and not shared is
// generating a unique db name for each.
string dbName = Guid.NewGuid().ToString();
// Create a fresh service provider, and therefore a fresh
// InMemory database instance.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Create a new options instance telling the context to use an
// InMemory database and the new service provider.
var builder = new DbContextOptionsBuilder<DeviceDbContext>();
builder.UseInMemoryDatabase(dbName)
.UseInternalServiceProvider(serviceProvider);
return builder;
}
Here is a very basic test case.
[Fact]
public void LocationExists_True()
{
Assert.True(_repository.LocationExists(_dbFixture.GoodLocationId));
}
I also made 8 of test cases that attempted to delete the same device with the same id and each passed.
From the documentation,
Typically, EF creates a single
IServiceProviderfor all contexts of a given type in an AppDomain - meaning all context instances share the same InMemory database instance. By allowing one to be passed in, you can control the scope of the InMemory database.
Instead of making the test class disposable and trying to dispose the data context that way, create a new one for each test:
private static DbContextOptions<BloggingContext> CreateNewContextOptions()
{
// Create a fresh service provider, and therefore a fresh
// InMemory database instance.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Create a new options instance telling the context to use an
// InMemory database and the new service provider.
var builder = new DbContextOptionsBuilder<DatabaseContext>();
builder.UseInMemoryDatabase()
.UseInternalServiceProvider(serviceProvider);
return builder.Options;
}
Then, in each test, new up a data context using this method:
using (var context = new DatabaseContext(CreateNewContextOptions()))
{
// Do all of your data access and assertions in here
}
This approach should get you a squeaky-clean in-memory database for each test.