단위 테스트에 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; }
}
답변
다음 호출은 메모리 내 데이터 저장소를 지 웁니다.
_context.Database.EnsureDeleted();
답변
파티에 조금 늦었지만 나도 같은 문제에 부딪 쳤지 만 결국 내가했던 것은.
각 테스트에 다른 데이터베이스 이름을 지정합니다.
optionsBuilder.UseInMemoryDatabase(Guid.NewGuid().ToString());
이렇게하면 추가 할 필요가 없습니다.
_context.Database.EnsureDeleted();
모든 테스트에서
답변
DbContextOptionsBuilder의 코드 정의를 다음과 같이 변경하기 만하면됩니다.
var databaseName = "DatabaseNameHere";
var dbContextOption = new DbContextOptionsBuilder<SchoolContext>()
.UseInMemoryDatabase(databaseName, new InMemoryDatabaseRoot())
.Options;
new InMemoryDatabaseRoot () 는 Id의 지속 문제없이 새 데이터베이스를 만듭니다. 따라서 지금은 필요하지 않습니다.
[TestCleanup]
public void Cleanup()
{
_context = null;
}
답변
나는 두 가지 대답의 조합으로 갈 것입니다. 테스트가 병렬로 실행되는 경우 다른 테스트를 실행하는 동안 데이터베이스가 삭제 될 수 있으므로 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();
}
}
답변
나는 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());
}
}
이 솔루션은 스레드로부터 안전합니다. 자세한 내용은 내 블로그 를 참조하십시오.
답변
여기에있는 예제는 RemoveRange를 통해이를 수행합니다. https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1
db.<Entity>.RemoveRange(db.<entity>);
답변
다음은 각 단위 테스트를 서로 격리하기위한 2 센트 접근 방식입니다. C # 7, XUnit 및 EF core 3.1을 사용하고 있습니다.
샘플 TestFixture 클래스.
public class SampleIntegrationTestFixture : IDisposable
{
public DbContextOptionsBuilder<SampleDbContext> SetupInMemoryDatabase()
=> new DbContextOptionsBuilder<SampleDbContext>().UseInMemoryDatabase("MyInMemoryDatabase");
private IEnumerable<Student> CreateStudentStub()
=> new List<Student>
{
new Student { Id = rng.Next(1,10000), Name = "Able" },
new Student { Id = rng.Next(1,10000), Name = "Bob" }
};
public void Dispose()
{
}
}
샘플 IntegrationTest 클래스
public class SampleJobIntegrationTest : IClassFixture<SampleIntegrationTestFixture >
{
private DbContextOptionsBuilder<SampleDbContext> DbContextBuilder { get; }
private SampleDbContext SampleDbContext { get; set; }
public SampleJobIntegrationTest(SampleIntegrationTestFixture
sampleIntegrationTestFixture )
{
SampleIntegrationTestFixture = sampleIntegrationTestFixture ;
SampleDbContextBuilder = sampleIntegrationTestFixture .SetupInMemoryDatabase();
}
[Fact]
public void TestMethod1()
{
using(SampleDbContext = new SampleDbContext(SampleDbContextBuilder.Options))
var students= SampleIntegrationTestFixture.CreateStudentStub();
{
SampleDbContext.Students.AddRange(students);
SampleDbContext.SaveChanges();
Assert.AreEqual(2, _context.Students.ToList().Count());
SampleDbContext.Database.EnsureDeleted();
}
}