Marqueone
Marqueone

Reputation: 1213

Entity Framework Core 2.2 not reseting after tear down while unit testing

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

Answers (1)

Ivan Stoev
Ivan Stoev

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

Related Questions