web-dev-qa-db-fra.com

Comment réinitialiser un fournisseur EF7 InMemory entre les tests unitaires?

J'essaie d'utiliser le fournisseur EF7 InMemory pour les tests unitaires mais la nature persistante de la base de données InMemory entre les tests me pose des problèmes.

Le code suivant illustre mon problème. Un test fonctionnera et l'autre test échouera toujours. Même si j'ai défini le _context sur null entre les tests, la deuxième exécution de test contiendra toujours 4 enregistrements.

[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; }
}
50
Sailing Judo

L'appel suivant effacera le magasin de données en mémoire.

_context.Database.EnsureDeleted();
65
natemcmaster

Un peu tard pour la fête, mais j'ai également rencontré le même problème, mais ce que j'ai fini par faire était.

Spécification d'un nom de base de données différent pour chaque test.

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

De cette façon, vous n'avez pas à ajouter

_context.Database.EnsureDeleted();

dans tous vos tests

18
R4nc1d

J'irais avec une combinaison des deux réponses. Si les tests s'exécutent en parallèle, vous pourriez avoir une base de données supprimée pendant que vous êtes en train d'exécuter un autre test, donc je voyais des échecs sporadiques lors de l'exécution de 30+ tests.

Donnez-lui un nom de base de données aléatoire et assurez-vous qu'il est supprimé une fois le test terminé.

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
bradlis7

J'utilise un appareil DbContext comme le suivant

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

vous pouvez maintenant simplement faire

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

Cette solution est thread-safe. Voir mon blog pour plus de détails.