EF Core:使用 InMemory 数据库进行测试具有不一致的行为

EF Core : Testing with InMemory Database has inconsistent behavior

我正在使用 InMemory 数据库测试我的 ASP .NET Core Web API 应用程序中的存储库层。 因此我有一个问题,在几个测试中,我设置了数据。但是,使用相同的代码,当我 运行 测试时,有时数据存在,有时不存在。我不明白为什么。

我正在使用 XUnit 测试框架。

这是我的测试:

public class UserRepositoryTest
    {
        private ApplicationDbContext context;

        void setup()
        {
            var options = new DbContextOptionsBuilder<ApplicationDbContext>()
                .UseInMemoryDatabase(databaseName: "ApplicationDatabase")
                .Options;

            this.context = new ApplicationDbContext(options);
            this.context.Database.EnsureDeleted();
        }

        [Fact]
        void GetUserByUsernameTest()
        {
            this.setup();
            // Given
            var manager = new User {Username = "Ombrelin", Email = "test@test.fr"};
            context.Users.Add(manager);
            context.SaveChanges();

            // When 
            var repo = new UserRepository(context);
            var user = repo.GetUserByUsername("Ombrelin");

            // Then
            Assert.Equal("Ombrelin", user.Username);
        }

        [Fact]
        void FindUsersByUsernameContainsTest()
        {
            this.setup();
            // Given
            var manager1 = new User {Username = "Arsène", Email = "test@test.fr"};
            var manager2 = new User {Username = "Jean Michel", Email = "test@test.fr"};
            context.Users.Add(manager1);
            context.Users.Add(manager2);
            context.SaveChanges();

            // When 
            var repo = new UserRepository(context);
            var users = repo.findUsersByUsernameContains("Ars");

            // Then
            Assert.Single(users);
        }

有人知道这件事吗?

提前致谢,

您正在多个测试中重复使用相同的数据库上下文。测试可以 运行 并行进行。因此,当使用相同的数据库上下文测试时,可能会影响彼此的结果。为避免这种情况,您需要通过让测试使用干净的数据库上下文来隔离测试:

public class UserRepositoryTest
{
    [Fact]
    public void GetUserByUsernameTest()
    {

        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: $"ApplicationDatabase{Guid.NewGuid()}")
            .Options;

        using(var context = new ApplicationDbContext(options))
        {
            // Given
            var manager = new User { Username = "Ombrelin", Email = "test@test.fr" };
            context.Users.Add(manager);
            context.SaveChanges();

            // When 
            var repo = new UserRepository(context);
            var user = repo.GetUserByUsername("Ombrelin");

            // Then
            Assert.Equal("Ombrelin", user.Username);
        }

    }
}

通过将唯一 ID 附加到数据库名称,您可以确保测试使用的是唯一的内存数据库。显然,这会使测试执行速度变慢。许多测试人员还使用不同的上下文来播种数据和执行测试:

public class UserRepositoryTest
{
    [Fact]
    public void GetUserByUsernameTestSeparateContexts()
    {

        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: $"ApplicationDatabase{Guid.NewGuid()}")
            .Options;

        using (var context = new ApplicationDbContext(options))
        {
            // Given
            var manager = new User { Username = "Ombrelin", Email = "test@test.fr" };
            context.Users.Add(manager);
            context.SaveChanges();

        }

        using (var context = new ApplicationDbContext(options))
        {
            // When 
            var repo = new UserRepository(context);
            var user = repo.GetUserByUsername("Ombrelin");

            // Then
            Assert.Equal("Ombrelin", user.Username);
        }

    }
}

这使得测试更加真实,因为播种数据的函数和使用数据的函数通常使用不同的上下文。还要记住,InMemoryProvider 不是关系数据库。因此,它不支持实际数据库服务器的某些功能,例如引用完整性检查、TimeStamp、IsRowVersion 等。查看 MS 文档了解详细信息:here.