如何在单元测试之间重置EF7 InMemory提供程序?

Sai*_*udo 52 unit-testing mstest entity-framework-core

我试图使用EF7 InMemory提供程序进行单元测试,但测试之间的InMemory数据库的持久性使我遇到问题.

以下代码演示了我的问题.一个测试将起作用,另一个测试将始终失败.即使我在测试之间将_context设置为null,第二次测试运行总会有4条记录.

[TestClass]
public class UnitTest1
{

    private SchoolContext _context;

    [TestInitialize]
    public void Setup()
    {
        Random rng = new Random();

        var optionsBuilder = new DbContextOptionsBuilder<SchoolContext>();
        optionsBuilder.UseInMemoryDatabase();

        _context = new SchoolContext(optionsBuilder.Options);
        _context.Students.AddRange(
            new Student { Id = rng.Next(1,10000), Name = "Able" },
            new Student { Id = rng.Next(1,10000), Name = "Bob" }
        );
        _context.SaveChanges();
    }

    [TestCleanup]
    public void Cleanup()
    {
        _context = null;
    }

    [TestMethod]
    public void TestMethod1()
    {
        Assert.AreEqual(2, _context.Students.ToList().Count());
    }

    [TestMethod]
    public void TestMethod2()
    {
        Assert.AreEqual(2, _context.Students.ToList().Count());
    }

}

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class SchoolContext : DbContext
{
    public SchoolContext(DbContextOptions options) : base(options) { }

    public DbSet<Student> Students { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

nat*_*ter 75

以下调用将清除内存中的数据存储.

_context.Database.EnsureDeleted();
Run Code Online (Sandbox Code Playgroud)

  • 这似乎不会重置内存数据库的标识列.因此,如果您使用行播种数据,则第一个测试将看到id为1的行,第二个测试2等等.这是设计的吗? (16认同)
  • 现在是2019年,即使删除数据库并重新创建数据库之后ID仍然存在的问题仍然是一个问题! (2认同)

R4n*_*c1d 18

派对迟到了,但我也遇到了同样的问题,但我最终做的是.

为每个测试指定不同的数据库名称.

optionsBuilder.UseInMemoryDatabase(Guid.NewGuid().ToString());
Run Code Online (Sandbox Code Playgroud)

那样你就不必添加

_context.Database.EnsureDeleted();
Run Code Online (Sandbox Code Playgroud)

在你所有的测试中

  • 这不是留在记忆中吗? (4认同)
  • 是的,它会存在于内存中,但是如果您将上下文包装在 using 语句中,它将自动被释放。 (4认同)
  • 除了最上面的答案之外,这可以用来解决识别列重置问题。我仍然会使用“EnsureDeleted”,只需要在“[TearDown]”方法中添加一次,该方法将在每次测试后运行,所以不会太痛苦。 (2认同)

Ami*_*med 12

只需将 DbContextOptionsBuilder 的代码定义更改为如下所示:

        var databaseName = "DatabaseNameHere";
        var dbContextOption = new DbContextOptionsBuilder<SchoolContext>()
                                    .UseInMemoryDatabase(databaseName, new InMemoryDatabaseRoot())
                                    .Options;
Run Code Online (Sandbox Code Playgroud)

new InMemoryDatabaseRoot()创建一个新数据库,而不会出现 Id 持久化的问题。所以你现在不需要:

       [TestCleanup]
       public void Cleanup()
       {
           _context = null;
       }
Run Code Online (Sandbox Code Playgroud)

  • +1 表示 InMemoryDatabaseRoot。但是,只需在每个 TestInitialize 中使用 TestCleanup 并将上下文设置为 null 并重新创建新上下文(假设您使用相同的数据库名称,并且不使用 InMemoryDatabaseRoot),将为您提供相同的内存数据库。 (3认同)

bra*_*is7 5

我会结合两个答案。如果测试并行运行,您可能会在运行另一个测试的过程中删除一个数据库,因此在运行 30 多个测试时我会看到零星的失败。

给它一个随机的数据库名称,并确保它在测试完成后被删除。

public class MyRepositoryTests : IDisposable {
  private SchoolContext _context;

  [TestInitialize]
  public void Setup() {
    var options = new DbContextOptionsBuilder<ApplicationDbContext>()
      // Generate a random db name
      .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
      .Options;
      _context = new ApplicationDbContext(options);
  }

  [TestCleanup]
  public void Cleanup()
    _context.Database.EnsureDeleted(); // Remove from memory
    _context.Dispose();
  }
}
Run Code Online (Sandbox Code Playgroud)