-
Notifications
You must be signed in to change notification settings - Fork 57
Rules for tests using EF Core
There are some rules to follow when when accessing a database via EF Core in xUnit test. This page explains each rule and how EF Core or EfCore.TestSupport can allow you to follow the five rules.
By default, xUnit will run each class containing tests in parallel, and every test method in a test class is then run in series. That means if you used the same database for all of your test, then many tests will be accessing / changing the database at the same time, which will make it impossible to check that the test works! There are two solutions:
- Use an in-memory database,
SqliteInMemory.CreateOptions<TContext>- see Using SQLite in-memory test databases - Use one of EfCore.TestSupport's methods that returns a database name that contains the class type name on the end. This means the test class has database that is unique to its test class. See
2. You need an empty database at the start of each test AND 3, the database schema matches the current EF Core Model
I have combined rules 2 and 3 because they are fixed by the same EfCore.TestSupport features, but here are the reasons:
- Rule 2 is there because it much harder to write tests that don't care what the database contains. EF Core and EfCore.TestSupport provides ways to clear out all the rows in your tables (and more).
- Rule 3 is there because if you change any of your entity classes or the EF Core configuration, then the current database schema (e.g. tables, views, function etc) will be out of date and most likely not work with your new . EF Core and EfCore.TestSupport provides wasy to delete the current schema and recreated the schema using EF Core's current Model of the database.
There are two ways to implement rule 2 and 3:
- Call EF Core
EnsuredDeletedmethod and then callEnsuredCreated- see the testTestEnsureDeletedEnsureCreatedOkin the TestSqlServerHelpers test class. - For SQL Server and PostgreSQL the
EnsuredDeleted+EnsuredCreatedapproach is a bit slow. Therefore EfCore.TestSupport has a method calledEnsureCleanwhich is quicker - see the testTestSqlDatabaseEnsureCleanOkin the TestSqlServerHelpers test class.
NOTE: SqliteInMemory databases are empty by default and EnsuredCreated will set up the correct schema.
By default EF Core will track all the entities that were read or written to the database. That's perfect for your application, but can hide errors in the database code you are testing. For instance you might write some data into the database in the SETUP, but that tracked data can cover over problems in your ATTEMPT, e.g. in SETUP you write a Book class with collection of Review classes. In the ATTEMPT stage you that Book and you forget the .Include(book => book.Reviews). Because of the tracked entities the Book will have the Reviews collection filled, when in real use the Reviews collection wouldn't be filled.
There are a few ways to fix this, but adding the following line of code context.ChangeTracker.Clear() at the end of the SETUP and ATTEMPT stage will stop this issue.
NOTE: See this section in the "Changes in EfCore.TestSupport 5" article for a deeper explanation.
You need a way to provide the database options to your DbContext. This is done via a constructor in your DbContext which has a DbContextOptions<YourDbContext> options parameter, which is normal for ASP.NET Core - see this example.
If you are using the OnConfiguring approach you need add if (!optionsBuilder.IsConfigured) into your OnConfiguring method - see this example.
- Testing against a PostgreSQL db
- Changes in EfCore.TestSupport 5
- Testing with production data
- Using an in-memory database (old)