EfCore.TestSupport icon indicating copy to clipboard operation
EfCore.TestSupport copied to clipboard

Issues with possible unintended re-use of SQLite in-memory DbContext?

Open kakins opened this issue 2 years ago • 5 comments

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 a MyDbContext IDateTimeAdapter.UtcNow of DateTime.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 same Mock<IDateTimeAdapter>

kakins avatar Jun 15 '22 20:06 kakins

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.

JonPSmith avatar Jun 16 '22 07:06 JonPSmith

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.

kakins avatar Jul 19 '22 13:07 kakins

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.

JonPSmith avatar Jul 20 '22 08:07 JonPSmith

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.

kakins avatar Jul 20 '22 14:07 kakins

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 🤞

kakins avatar Aug 16 '22 20:08 kakins