Entity Framework core has really simplified working with databases and has drastically improved the testability aspects of these databases.
Providing developers the ability to create their databases as separate class library projects
As any developer will tell you releasing a product or library without testing it is generally accepted as an unforgivable sin in the software development world. Therefore if you’re making use of the Code-First database development process, enabled via Entity Framework then you should also ensure you at least make an attempt to write Unit & Integration tests for your databases.
What are Unit tests
Unit tests are used to check if small units of code are functioning as you would expect them to. Typically this testing is done as part of the development process, and a unit test will check that the code being tested meets a specification, while a library of unit tests together will check the functions expected of the application. The process is usually automated, which allows the library of tests to be re-run frequently, allowing us to find bugs earlier, and in smaller units of code, which are easier to debug.
Unit Tests enable developers to think and reason about their code in a modular fashion. They are frequently combined with a continuous integration system, to regression test changes, and help speed up and minimize the risk from refactoring.
Unit testing can also form the basis of documentation as the functional requirements should tie to unit tests, and vice versa, allowing you to see which requirements are met by the developed code.
My two favourite books on Unit Testing : The Art of Unit Testing : With Examples in C# & Clean Code: A Handbook of Agile Software Craftsmanship explain how Unit testing, done right, can be the difference between a failed project and a successful one, between a maintainable code base or a big ball of mud.
Using In Memory Databases in Unit tests
When developing Unit or Integration Tests you don’t want to use a physical database on a server, because you don’t necessarily want to concern yourself with ceremony or administration tasks of maintaining the database server. Fortunately, Entity Framework core provides a couple of different in-memory database options to assist in developing tests.
These two choices for in-memory database providers depend on whether or not you’re using
Foreign Key Constraints
If you don’t really want to or need to worry about Relational database characteristics then you can make use of the
Microsoft.EntityFrameworkCore.InMemory or if you would prefer a simulation closer to the real thing then
Microsoft.EntityFrameworkCore.Sqlite should be your library of choice.
In order to provide examples of using both these options I will use the unit tests of my Generic Repository Nuget Package .
All code in the examples make use of my current favourite Unit Testing Framework xUnit, but should be fairly easy to convert to any of the others.
Testing Entity Framework Core using In-Memory Database Provider
To use the In-Memory database provider first we need to add the following nuget package :
In order to create an instance of a
DbContext to use for our tests, we create an instance of
DbContextOptions. To create an instance of
DbContextOptions use the
A key point to take note of is that we generate a
GUID to use as a name for the database. This is to ensure that every
TestContext run has new database that is not affected by other test runs.
Although not completely necessary I make use of Class Fixtures to share contexts between tests.
Testing Entity Framework Core Relational Databases using SQLite In-Memory Mode
In order to use the SqLite provider you will first need to add a reference your Unit Test Project.
The SQLite provider itself is a relational database provider, However we can take advantage of
SQLite in-memory mode. The key benefit is that we get the full behavior of a relational database, with the benefits of the running in-memory.
By default each SQLite connection using the
":memory:" connection string will use a different in-memory database, so we don’t need to generate a new name for each connection.
An Additional step required for the
SQLite provider is to call both the
EnsureCreated methods on the context’s Facade, else we’ll get DbUpdateException: “SQLite Error 1: ‘no such table: ” exception .
Limitations of In-Memory Databases
In-memory databases are great for unit testing and can really assist in providing the ability to re-create Unit Testing databases. However, there are a few limitations to using In-Memory databases like SqLite that you should be aware of. Particulary if the
DbContext your project is using is configured to use some advanced Database features, this is due to the fact that most In-Memory databases do not support advanced Relational Database Management System (RDBMS) features.
|User Defined Functions (UDF)||Different|
|Fragment Default Values||Different|
You should also check out SQLite EF Core Database Provider Limitations, as most of the limitations are a result of limitations in the underlying SQLite database engine and are not specific to EF Core.
You should exercise caution when developing tests making use of In-Memory databases especially if the conditions you’re testing explicitly make use of advanced database features. It is usually the case that you should reconsider your Unit testing strategy and confirm whether you are actually attemtpting to write Integration tests. Check out Roy Osheroves – The Art of Unit Testing : With Examples in C#
Entity Framework core has made it really easy to set up and configure in-memory database options, reducing the amount of ceremony and configuration one has to do to get it up and running.
Being able to quickly and easily replicate and simulate database inteactivity in your Unit and Integration tests helps to ensure your code operates and functions as expected in ll conditions.
Denizon's product line successfully integrate IoT, Artificial Intelligence and Blockchain technology to enable efficient, productive, secure and scalable solutions to help organisations address increasing energy demands, ecological impact and Health & Safety concerns of their staff.