【问题标题】:EF Core : Testing with InMemory Database has inconsistent behaviorEF Core:使用 InMemory 数据库进行测试的行为不一致
【发布时间】:2020-05-31 17:07:40
【问题描述】:

我正在使用 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);
        }

有人知道吗?

提前致谢,

【问题讨论】:

  • 旁白:xUnit 将在每次测试之前调用类构造函数。不需要setup 方法,只需将其移至构造函数中即可

标签: c# unit-testing asp.net-core .net-core in-memory-database


【解决方案1】:

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

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

【讨论】:

  • 我不相信 xunit 会在同一个类中并行运行测试。他们需要在不同的班级。在这些情况下,您可以使用 Collection 属性来确保它们一起运行
  • 是的,你是对的。根据我假设的帖子,有多个测试类,并且在示例类中没有定义测试集合属性。这意味着 xunit 的默认行为适用:每个类一个测试集合。根据我基于xunit.net/docs/running-tests-in-parallel.html 的理解,不同类中的测试在这种情况下并行运行。如果所有类的构建都与示例类相似,这将导致 EF Core 出现问题。
  • 是的,我认为@Marc 是对的,因为我有多个课程,而且问题主要发生在我的 CI 运行所有测试时。我应用了你的例子,它似乎工作得很好
猜你喜欢
  • 2021-12-17
  • 2021-04-20
  • 1970-01-01
  • 2019-01-25
  • 2017-10-19
  • 2016-12-17
  • 2022-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多