This document provides information when converting from EfCore.TestSupport version 3.2.0 to EfCore.TestSupport version 5 or higher.
NOTE: EfCore.TestSupport version 5 doesn't work with any EF Core version below 5.0.1.*
The first thing to say is you don't need to upgrade to EfCore.TestSupport version 5. EfCore.TestSupport, version 3.2.0 will work with EF Core 5, apart from the EfSchemaCompare feature, which now has separate library. To do this you have to load the EF Core 5's NuGet packaages, which will take precedence over the versions in the EfCore.TestSupport library.
If you are using EfCore.TestSupport's SqliteInMemory.CreateOptions` methods, then a lot of unit tests could break. Read the rest of this document to decide is you want to upgrade.
- BREAKING CHANGE: The
SqliteInMemory.CreateOptions
etc. has changed and some of your unit tests might break. See section below. - Nice new
EnsureClean
feature added for SQL Server. See docs on that. - The EfSchemaCompare feature has been removed. If you need this then keep using the V3 version of EfCore.TestSupport (I do plan to create a library for EfSchemaCompare, but I haven't done that yet)
- Added SQLite/SQL Server options with logging using the new
LogTo
logging output and marked the...WithLogging
versions as obsolete. - Cosmos DB methods renames and extended.
- Removed InMemory Database helper as this provider isn't a good way to unit test. If you need it then use EF Core's In Memory database provider.
- Removed SeedDatabase - use old 3.2.0 EfCore.TestSupport version (very few people used this)
If you use any of the SqliteInMemory.CreateOptions...
from previous versions of EfCore.TestSupport, then its VERY LIKELY THAT IT WILL BREAK YOUR UNIT TESTS!!
The new SqliteInMemory.CreateOptions<MyDbContext>()
method now returns a class that implements DbContextOptions<MyDbContext>
needed by the database, but now also implements IDisposable
. This is done to dispose Sqlite's connection (which contains the in-memory data) when the context is disposed. (I didn't do that in the previous versions, and I should have.)
This means when the application DbContext using that option is disposed the Dispose
method in the options class is also called and the connection is disposed.
This means if you have a unit tests like the one below it will fail, because the second instance of the dbContext the connection will be empty.
public void TestSqliteTwoInstancesBAD()
{
//SETUP
var options = SqliteInMemory.CreateOptions<BookContext>();
using (var context = new BookContext(options))
{
context.Database.EnsureCreated();
context.SeedDatabaseFourBooks();
}
using (var context = new BookContext(options))
{
//ATTEMPT
//THIS WILL FAIL!!!! THIS WILL FAIL!!!! THIS WILL FAIL!!!!
var books = context.Books.ToList();
//VERIFY
books.Last().Reviews.ShouldBeNull();
}
}
You have four options:
- Quick and Easy: Turn off Dispose using
options.TurnOffDispose()
- Best approach: Have one instance of the application DbContext and use
ChangeTracker.Clear()
. - Keep your Using(var...: Have two instances of the application DbContext and call
options.StopNextDispose()
- Lots of DbContext instances: Turn off Dispose and manually dispose at the end
The quick version is to called options.TurnOffDispose()
. This makes it work in the same way the previous versions of EfCore.TestSupport worked.
public void TestSqliteThreeInstancesOk()
{
//SETUP
var options = SqliteInMemory.CreateOptions<BookContext>();
options.TurnOffDispose();
//... rest of your code as it was
}
In EF Core 5 there is new feature triggered by context.ChangeTracker.Clear()
. This clears out all the tracked entities in the current instance of the application DbContext. This means you don't need two application DbContexts to check that your second part worked property.
public void TestSqliteOneInstanceWithChangeTrackerClearOk()
{
//SETUP
var options = SqliteInMemory.CreateOptions<BookContext>();
using var context = new BookContext(options);
context.Database.EnsureCreated();
context.SeedDatabaseFourBooks();
context.ChangeTracker.Clear(); //NEW LINE ADDED
//ATTEMPT
var books = context.Books.ToList();
//VERIFY
books.Last().Reviews.ShouldBeNull();
}
NOTE: It also allows you to use using var context = ...
- see line 5. This makes the code simpler, and quicker to write. That's why I recommend this version
If you want to keep the two instances of the application DbContext, then you need to use the options.StopNextDispose()
to stop the dispose on the first application DbContext instance. You can call the StopNextDispose
method any time before the first application DbContext instance is disposed, but I tend to do it right under the creating of the option, as shown in line 5 of the code below
public void TestSqliteTwoInstancesGood()
{
//SETUP
var options = SqliteInMemory.CreateOptions<BookContext>();
options.StopNextDispose();
using (var context = new BookContext(options))
{
context.Database.EnsureCreated();
context.SeedDatabaseFourBooks();
}
using (var context = new BookContext(options))
{
//ATTEMPT
var books = context.Books.ToList();
//VERIFY
books.Last().Reviews.ShouldBeNull();
}
}
If you have multiple instances of the application DbContext, then you can use the options.TurnOffDispose()
and call the options.ManualDispose()
methods at the end of the unit test.
public void TestSqliteThreeInstancesOk()
{
//SETUP
var options = SqliteInMemory.CreateOptions<BookContext>();
options.TurnOffDispose();
using (var context = new BookContext(options))
{
context.Database.EnsureCreated();
context.SeedDatabaseFourBooks();
}
using (var context = new BookContext(options))
{
//ATTEMPT
var books = context.Books.ToList();
//VERIFY
books.Last().Reviews.ShouldBeNull();
}
using (var context = new BookContext(options))
{
//ATTEMPT
var books = context.Books.ToList();
//VERIFY
books.Last().Reviews.ShouldBeNull();
}
options.ManualDispose();
}
NOTE the call to options.TurnOnDispose();
before the last application DbContext.
- You can find the EfSchemaCompare feature in the GitHub repo EfCore.SchemaCompare and its associated NuGet package.
- The InMemory Database provider is not recommended by the EF Core team - see this link .
- Removed SeedDatabase - very few people used this, but you can get the code in the Version3-2-0 branch.