Подтвердить что ты не робот

Как я могу reset поставщик EF7 InMemory между модульными тестами?

Я пытаюсь использовать поставщик EF7 InMemory для модульных тестов, но постоянный характер базы данных InMemory между тестами вызывает у меня проблемы.

Следующий код демонстрирует мою проблему. Один тест будет работать, и другой тест всегда терпит неудачу. Даже если я установил _context для нуля между тестами, во втором тестовом прогоне всегда будет 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; }
}
4b9b3361

Ответ 1

Следующий вызов очистит хранилище данных в памяти.

_context.Database.EnsureDeleted();

Ответ 2

Бит опоздал на вечеринку, но я также столкнулся с той же проблемой, но то, что я закончил, было.

Указание имени каждой базы данных для каждого теста.

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

Таким образом вам не нужно добавлять

_context.Database.EnsureDeleted();

во всех ваших тестах

Ответ 3

Я использую прибор 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());
    }
}

Это решение является поточно-ориентированным. Подробности смотрите в моем блоге.

Ответ 4

Я бы пошел с комбинацией обоих ответов. Если тесты выполняются параллельно, вы можете удалить базу данных, когда выполняете другой тест, поэтому при выполнении тестов 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();
  }
}

Ответ 5

Просто измените определение кода DbContextOptionsBuilder, чтобы оно было следующим:

        var databaseName = "DatabaseNameHere";
        var dbContextOption = new DbContextOptionsBuilder<SchoolContext>()
                                    .UseInMemoryDatabase(databaseName, new InMemoryDatabaseRoot())
                                    .Options;

new InMemoryDatabaseRoot() Создает новую базу данных без сохранения идентификатора. Так что теперь вам не нужно:

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