This post is about writing integration tests and TestContainers. As you might know writing integration tests against the in-memory provider is a bad practice. Because it only works well with trivial solutions. Mostly because the in-memory API of
IQueryProvider doesn’t match the LINQ query provider. Here’s a good article called “Avoid In-Memory Databases for Tests” by Jimmy Bogard that goes into depth about this problem.
But what is a good solution if you want to integration test your, APIs that are backed by, let’s say SQL server database? One option is to create an actual database and manage the data creation and deletion for each integration test there. Even though that totally possible, it is rather a cumbersome solution. But there’s an easier solution to this that we’re going to discuss in this post. We can use docker containers to run an instance of SQL server for us as long as the test is running, and then delete the container when our tests are finished, and we no longer need it.
Setting up Asp.Net Core 6 Integration Test with TestContainers
The first step is to create our
IntegrationTestFactory classing using what Asp.Net Core 6 provides to us. In this case we’re going to use the class
Microsoft.AspNetCore.Mvc.Testing namespace. We’re going to inherit from this class and setup our webhost. This webhost is going to be responsible to basically run our integration tests against our API. We’ll set it up like so:
Here we first remove the previous
DbContext from our DI injection graph and register a new one that points to the connection string that we’re going to create in our container.
Setting up the TestContainers with Sql Server Image
The next step is to create our test container. In order to have a reference to
TestcontainerDatabase class, we need to install the
After Installing the package, we can setup our test container like so:
Here we use the
TestcontainersBuilder and pass the type
MsSqlTestcontainer as its generic argument. Then we’re going to create an instance of
MsSqlTestcontainerConfiguration and in it we’re going to specify the password. In this case setting up the username and port and database name is not possible, if we set them, we’re going to receive
NotImplementedException. Since we use Entity Framework Code First with SQL server, we’re going to use a SQL server image. This image (“
mcr.microsoft.com/mssql/server:2022-latest“) is going to be pulled and run inside the docker container.
There are also two extra methods called
DisposeAsync that is required to be implemented when we inherit from
WebApplicationFactory. These two methods are going to be called whenever the app starts and ends. In the app start we’re going to start the container by calling
_container.StartAsync(). When the app ends, we’re going to call
IntegrationTestFactory class is going to look something like this.
Creating IntegrationTestBase to ease the Integration test Creation
One extra step is to create a base class for all of our integration tests. In this class we’re going to create the things that we’re going to use often in our integration tests. More specifically in this class we’re going to create
IntegrationTestFactory and our db context and make it available to our inheritors. Here’s all the code for this class.
Writing Some Integration Tests Using TestContainers
Now we can start writing some integration tests that will use the test containers to run a SQL server image inside docker. Here’s how the class and its constructor will look like.
Finally, here’s a test that we’ve written using the things we’ve created so far.
Here’s a picture of the images that are running inside the docker once we run our test.
If you want to experiment with this code and the tests you can clone CuratedArt repository and take a look. Don’t forget to install docker before running the tests.
In this post we saw how we can use
TestContainers package to run our integration tests in docker using the latest SQL server 2022 image. If you want to check the codes mentioned in this post, you can visit CuratedArt repository and go to its integration tests project.