When you're working on an application which uses some sort of database, there is always a point when you need to test some functionality directly on the database server - e.g. complex LINQ queries, transactions handling etc. In this post, we will setup the testing project for an app, where Entity Framework and code-first approach are used. Our target is simple - when tests runner is triggered, a testing db will be created, migrations will be applied and after all the tests will be executed, the db will be deleted.
For this post, I've created a simple demo which keeps track of employees and their business trips. The solution contains only
Dal project and is available on github - ef-codefirst-database-testing.
The model is simple - there are Employee
and BusinessTrip
entities with many-to-many relationship.
DAL project implements EmployeesService
and BusinessTripsService
classes, which provide database related logic on the top
of the database model. It uses Entity Framework, so it implements DatabaseContext
class for communication with DB.
For testing, we add another project in the demo solution and call it Dal.Tests. It references the DAL project
and uses xUnit
testing framework. We want to test the functionality implemented in the EmployeesService.cs
and BusinessTripsService.cs
classes, and we want to test it on a testing database, but with the same setup as in production.
So we create EmployeesServiceTests.cs
and BusinessTripsServiceTests.cs
classes with appropriate testing methods.
Now we need to find a way how to setup the db before any of the tests will run, execute the tests on the testing db, and delete the db after testing.
It's really simple to create a properly initialized testing database from scratch. Everything we need to do are the following steps.
Before I wrote this post, I used different and more complicated method for initialization of testing database. It involved sql commands for database creation and custom migrator class responsible for running migrations from Dal project. During the demo project implementation I discovered much simpler way, which I am gonna show you now.
App.config
name
should be the same as defined in DatabaseContext
constructor.
We want to use (LocalDb)\MSSQLLocalDB as data source -
that way, we don't need a full instance of SQL Server Express running on the testing environments.
AttachDbFilename setting specifies the path and name of the database file and finally, the initial catalog setting specifies the name
of the testing database in the SQL server instance catalog. context.Database.Create()
method creates the db and run the migrations from the Dal project. By default, the database is
created in the C:\Users\%username% folder. This could be a problem when the tests are running on a CI server (access rights, folder existence...). For that reason,
with AppDomain.CurrentDomain.SetData
we setup the DataDirectory
to current AppDomain and the database will be created in the bin
folder.
Now we know how to create the testing database. However, where should we create it? When we put the code into the BusinessTripsServiceTests
class constructor, the database will be created for every test method from this class separately (xUnit creates new instance of class for every test). This will terribly slow down the tests execution and because the tests
run in parallel by default, it can also cause tests crashing.
The xUnit
framework provides a functionality called fixtures,
which allows to use the same test context across multiple tests.
For the methods in the same class, we can implement Class Fixture.
That way, the database will be created only once per all tests in the class, which is better, but not exactly what we want.
What we need to use is a Collection Fixture. That way, we can create a single test context with one testing db
and share it among tests in several test classes, and have it cleaned up after all the tests in the test classes have finished.
The TestDbFixture
implementation should look like this.
Then create the collection definition class with proper [CollectionDefinition]
attribute, which will identify the test collection.
And add the [Collection]
attribute to all the test classes that will use the testing database.
You can check the full implementation in my demo project ef-codefirst-database-testing.
This way, all the tests will use the same testing database. It will be created before execution of the first test from the collection, and it will be deleted after all the tests in the collection have finished. And that's exactly the thing we wanted to achieve.
I created this technical article for Signals company in cooperation...
I created this technical article for Signals company in cooperation...
Many of us use the async/await feature in C# projects,...