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

问题描述 投票:45回答:3

我试图使用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; }
}
unit-testing mstest entity-framework-core
3个回答
65
投票

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

_context.Database.EnsureDeleted();

18
投票

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

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

optionsBuilder.UseInMemoryDatabase(Guid.NewGuid().ToString());

这样你就不必添加了

_context.Database.EnsureDeleted();

在你所有的测试中


1
投票

我会结合两个答案。如果测试是并行运行的,那么当您正在运行另一个测试时,可能会删除一个数据库,因此在运行30多个测试时我看到了零星的故障。

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

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();
  }
}

1
投票

我使用如下的DbContext夹具

public class DbContextFixture 
    where TDbContext : DbContext
{
    private readonly DbContextOptions _dbContextOptions = 
        new DbContextOptionsBuilder()
            .UseInMemoryDatabase("_", new InMemoryDatabaseRoot())
            .Options;

    public TDbContext CreateDbContext()
    {
        return (TDbContext)(typeof(TDbContext)
            .GetConstructor(new[] { typeof(DbContextOptions) })
            .Invoke(new[] { _dbContextOptions }));
    }
}

你现在可以干脆做

public class MyRepositoryTests : IDisposable {
    private SchoolContext _context;
    private DbContextFixture<ApplicationDbContext> _dbContextFixture;

    [TestInitialize]
    public void Setup() {
        _dbContextFixture = new DbContextFixture<ApplicationDbContext>();
        _context = _dbContextFixture.CreateDbContext();
        _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.Dispose();
        _dbContextFixture = null;
    }

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

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

此解决方案是线程安全的。有关详细信息,请参阅我的blog

© www.soinside.com 2019 - 2024. All rights reserved.