EfCore.TestSupport
EfCore.TestSupport copied to clipboard
Issues with possible unintended re-use of SQLite in-memory DbContext?
Does SQLite in-memory by default re-use DbContext instances?
I'm not sure if this is a more general SQLite issue or related specifically to EfCore.TestSupport.
Here's my general test setup...
public class MyDbFixture
{
public MyDbFixture()
{
// A custom `IDateTimeAdapter` to make testing easier with .NET DateTime
var now = DateTime.UtcNow;
TestDateTime = new Mock<IDateTimeAdapter>();
TestDateTime
.SetupGet(dt => dt.UtcNow)
.Returns(now);
}
// All db tests will use this method to create the DbContext
// The same mocked instance of `IDateTimeAdapter` is passed in, used for setting default values for date columns
public MyDbContext CreateDbContext()
{
var options = SqliteInMemory.CreateOptions<MyDbContext>();
var context = new MyDbContext(options, TestDateTime);
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
return context;
}
}
Running the following tests presents no problem....
public class MyTests1
{
public MyTestsA(MyDbFixture fixture)
{
_fixture = fixture;
}
public async Task MyTest1()
{
using var dbContext = _fixture.CreateDbContext()
.... do testing stuff
}
public async Task MyTest2()
{
using var dbContext = _fixture.CreateDbContext()
.... do testing stuff
}
}
However, adding this test will cause the previous tests to occasionally fail when all tests are run together
public class MyTestsB
{
public async Task MyOtherTest()
{
var options = SqliteInMemory.CreateOptions<MyDbContext>();
// No Mock Setup for IDateTimeAdapter
using var context = new OrderPollingDbContext(options, new Mock<IDateTimeAdapter>().Object);
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
.... do testing stuff
}
}
I notice the following:
- Occasionally
MyTestsA
will have aMyDbContext
IDateTimeAdapter.UtcNow
ofDateTime.MinValue
. This is unexpected. - When I disable
MyTestsB
,MyTestsA
stop failing. -
MyTestsA
only continue to pass, even if DbContext is reused, because each test is using the sameMock<IDateTimeAdapter>
Hi @kakins,
I think your problem is because the connection is closes by EFCore.TestSupport. That's because theSqliteInMemory
static methods creates an disposable version of the DbContextOptions<TContext>
options. When the context is disposed the options are disposed which closes the connection, which will dispose of the in-memory database.
I suggest you try turning off the dispose using the code below
var options = SqliteInMemory.CreateOptions<BookContext>();
options.TurnOffDispose();
For more information / appraoches have a look at Working with multiple context instances - WARNING!.
Please close this issue if this fixes your problem.
Ok I will give that a try shortly.
But I'm a little confused. If the connection is disposed, that seems to be the opposite of what I'm experiencing. It seems like the context is not being disposed properly, but rather the "wrong" instance of a DbContext is being used in unrelated tests.
Sorry, but Its hard to diagnose your problem from what you say.
I suggest you try creating the DbContext (without turning off the dispose) in each test and see whether that works. That will tell you if the problem is because you are creating the fixture.
Understood. It can be a little hard to describe also. But in short it seems like the "wrong" db context is sometimes used in each test, when running multiple tests in parallel using the VS test runner. I've seen this with a fixture and without a fixture, when I am creating a new db context per test case. I'll see if I can put together a little sample project or something to demonstrate the issue more clearly.
Just updating here to let you know I haven't forgotten. I'm still seeing this issue in multiple projects but just haven't had time to put together a small repro. I will try to do it eventually 🤞