Reputation: 1213
I'm attempting to write some unit tests to cover as much of my api as possible, but instead of using a database I've decided to use the InMemory provider, not to test that EF works, rather that my implantation is correct, but what I'm running into is a strange side effect.
Essentially what is happening is the id values for objects that are seeded, are not resetting after each test.
In MSTest I've implemented a setup to create a new database context for each test, providing a new name for each test run and am making sure to call EnsureCreated()
before seeding my database. In addition I'm also implementing a TearDown to ensure that the database is correctly disposed.
[TestClass]
public class CountryTests
{
private CdiContext Context { get; set; }
[TestInitialize]
public void Setup()
{
DbContextOptions<CdiContext> options = new DbContextOptionsBuilder<CdiContext>()
.UseInMemoryDatabase(databaseName: $"{Guid.NewGuid()}")
.Options;
Context = new CdiContext(options);
Context.Database.EnsureCreated();
Seed();
}
[TestCleanup]
public void TearDown()
{
Context.Database.EnsureDeleted();
}
private void Seed()
{
var service = new CoreDataService(Context);
var regions = new List<Region>
{
new Region
{
Name = "Asia",
State = RecordState.Active,
Created = DateTime.Now,
UserId = Guid.NewGuid().ToString()
},
new Region
{
Name = "Africa",
State = RecordState.Active,
Created = DateTime.Now,
UserId = Guid.NewGuid().ToString()
},
new Region
{
Name = "South America",
State = RecordState.Active,
Created = DateTime.Now,
UserId = Guid.NewGuid().ToString()
}
};
Context.Regions.AddRange(regions);
Context.SaveChanges();
}
[TestMethod]
public async Task CanAddCountry()
{
//-- arrange
var service = new CoreDataService(Context);
var userId = "133BC82D-FDE2-4124-9207-CD3465511AEB";
var region = await service.GetRegionById(1);
var country = new Country
{
Name = "Canada",
State = RecordState.Active,
UserId = userId,
Created = DateTime.Now,
RegionId = region.Id
};
//-- act
var results = await service.AddCountry(country);
//-- assert
Assert.IsTrue(results.Name == "Canada");
Assert.IsTrue(results.UserId == "133BC82D-FDE2-4124-9207-CD3465511AEB");
Assert.IsNotNull(results.Region);
}
[TestMethod]
public async Task CanGetCountries()
{
//-- arrange
var service = new CoreDataService(Context);
var userId = "133BC82D-FDE2-4124-9207-CD3465511AEB";
var region = service.GetRegionById(1);
var names = new[] {"Canada", "Wales", "Japan", "Australia", "Hawaii", "Germany"};
for (var x = 0; x < 5; x++)
{
await service.AddCountry(new Country
{
Name = names[x],
State = RecordState.Active,
UserId = userId,
Created = DateTime.Now,
RegionId = region.Id
});
}
//-- act
var results = await service.GetCountries();
//-- assert
Assert.IsNotNull(results);
Assert.IsTrue(results.Count == 5);
}
[TestMethod]
public async Task CanDeleteCountry()
{
//-- arrange
var service = new CoreDataService(Context);
var userId = "133BC82D-FDE2-4124-9207-CD3465511AEB";
var region = service.GetRegionById(1);
var names = new[] { "Canada", "Wales", "Japan", "Australia", "Hawaii", "Germany" };
for (var x = 0; x < 5; x++)
{
await service.AddCountry(new Country
{
Name = names[x],
State = RecordState.Active,
UserId = userId,
Created = DateTime.Now,
RegionId = region.Id
});
}
//-- act
await service.DeleteCountry(id: 2, userId: Guid.NewGuid().ToString());
var countries = await service.GetCountries();
var country = await service.GetCountryById(2);
//-- assert
Assert.IsTrue(countries.Count != 5);
Assert.IsNull(country);
}
}
Now when I run this, the third test fails, because it can't find the correct id, and this to me is a bit weird.
I initially thought this was a problem with MSTest, but noticed the same behaviour in xUnit as well.
public class CdiTestBase : IDisposable
{
protected readonly CdiContext _context;
public CdiTestBase()
{
var options = new DbContextOptionsBuilder<CdiContext>()
.UseInMemoryDatabase(databaseName: $"{Guid.NewGuid()}")
.Options;
_context = new CdiContext(options);
_context.Database.EnsureCreated();
SeedRegions(_context);
}
public void Dispose()
{
_context.Database.EnsureDeleted();
_context.Dispose();
}
private void SeedRegions(CdiContext context)
{
var regions = new List<Region>
{
new Region
{
Name = "Asia",
State = RecordState.Active,
Created = DateTime.Now,
UserId = Guid.NewGuid().ToString()
},
new Region
{
Name = "Africa",
State = RecordState.Active,
Created = DateTime.Now,
UserId = Guid.NewGuid().ToString()
},
new Region
{
Name = "South America",
State = RecordState.Active,
Created = DateTime.Now,
UserId = Guid.NewGuid().ToString()
}
};
context.Regions.AddRange(regions);
context.SaveChanges();
}
}
public class CountryTests : CdiTestBase
{
[Fact]
public async Task CanAddCountry()
{
//-- arrange
var service = new CoreDataService(_context);
var userId = "133BC82D-FDE2-4124-9207-CD3465511AEB";
var region = await service.GetRegionById(1);
var country = new Country
{
Name = "Canada",
State = RecordState.Active,
UserId = userId,
Created = DateTime.Now,
RegionId = region.Id
};
//-- act
var results = await service.AddCountry(country);
//-- assert
Assert.True(results.Name == "Canada");
Assert.True(results.UserId == "133BC82D-FDE2-4124-9207-CD3465511AEB");
Assert.NotNull(results.Region);
}
[Fact]
public async Task CanGetCountries()
{
//-- arrange
var service = new CoreDataService(_context);
var userId = "133BC82D-FDE2-4124-9207-CD3465511AEB";
var region = service.GetRegionById(1);
var names = new[] { "Canada", "Wales", "Japan", "Australia", "Hawaii", "Germany" };
for (var x = 0; x < 5; x++)
{
await service.AddCountry(new Country
{
Name = names[x],
State = RecordState.Active,
UserId = userId,
Created = DateTime.Now,
RegionId = region.Id
});
}
//-- act
var results = await service.GetCountries();
//-- assert
Assert.NotNull(results);
Assert.True(results.Count == 5);
}
[Fact]
public async Task CanDeleteCountry()
{
//-- arrange
var service = new CoreDataService(_context);
var userId = "133BC82D-FDE2-4124-9207-CD3465511AEB";
var region = service.GetRegionById(1);
var names = new[] { "Canada", "Wales", "Japan", "Australia", "Hawaii", "Germany" };
for (var x = 0; x < 5; x++)
{
await service.AddCountry(new Country
{
Name = names[x],
State = RecordState.Active,
UserId = userId,
Created = DateTime.Now,
RegionId = region.Id
});
}
//-- act
await service.DeleteCountry(id: 2, userId: Guid.NewGuid().ToString());
var countries = await service.GetCountries();
var country = await service.GetCountryById(2);
//-- assert
Assert.True(countries.Count != 5);
Assert.Null(country);
}
}
If I run all the tests at the same time, I consistently get test failures, but if I run each test individually they all pass, and this bothers me.
Upvotes: 1
Views: 803
Reputation: 205619
This is a known issue with in-memory database auto generated keys, tracked by #6872 InMemory: Improve in-memory key generation.
It's been fixed in the upcoming 3.0 release - Each property uses independent in-memory integer key generation:
Old behavior
Before EF Core 3.0, one shared value generator was used for all in-memory integer key properties.
New behavior
Starting with EF Core 3.0, each integer key property gets its own value generator when using the in-memory database. Also, if the database is deleted, then key generation is reset for all tables.
Why
This change was made to align in-memory key generation more closely to real database key generation and to improve the ability to isolate tests from each other when using the in-memory database.
There is nothing you can do currently except to update the test code to not rely on hardcoded auto generated values (which is not good anyway).
Upvotes: 3