Dotnet6 Integration Testing with Sqlite Database
Integration testing provides different application components functionality working accurately together. Dotnet6 supports integration tests using different unit test frameworks. In this article, we will be implementing an integration test using an sqlite database. An sqlite db can be an effective way to test an EF Core application. In unit tests, components are mocked. On the contrary, integration tests ensures all related components and their infrastructures work properly without mocking. Although integration tests are slower than unit tests, all components combined together while testing.
Brief Info About Testing
Below we will elaborate more on the testing types, namely, unit testing, functional testing and integration testing.
Unit testing
The smallest part of a project is called unit which can be a function, a class or any other component. The main purpose of unit testing is to guarantee that every independent/individual component of the software is working as designed.
Functional testing
On the other hand, functional test is result-oriented and tests whether the features are working. The main goal is to ensure a functionality works as it is supposed to. The correctness of the used data is not very important while testing.
Integration testing
Integration testing can be used to test the components which are interdependent. The aim is to piece together the different modules of the software and ensure their correctness. While methods can pass the unit tests in individual basis, some issues may arise when they are combined. With the help of integration testing, these problems can be detected and solved before deployment.
SUT (system under test) Project
Now create an example SUT project. In the sample project we will retrieve a user list via web api. Firstly, we create a repository class which includes crud operations and DemoContext configurations which uses SqlServer in “Program.cs”
Then, we will define User entity, UserDto and their mappings.
Infrastructure
Since we will be using only one user table in this sample project, the DBContext only contains User entity.
After that, we will create repository which includes read part of crud operations for our example
Lastly, api controller will be created
Let the tests begin!
The test requires followings:
A test project with reference of SUT.
A web host for the SUT and a test client server communicates with the SUT.
NUnit as test adapter and runner.
In order to create a unit test, we need to complete all listed steps below.
- Configure web host
- Use a test server client for requests.
- Prepare request parameters
- Create a request and receive the response
- Make assertion
- Wait for all test executed and get reported results
DemoContextSqlite
Different migrations are created to be used in sqlite. The reason for this is that different migrations are created for each db provider. Types in sql server may have different equivalents in sqlite, same functionality may not be supported. So, we created a custom DbContext named DemoContextSqlite.
TestWebApplicationFactory
TestWebApplicationFactory is a custom implementation of WebApplicationFactory. The necessary settings for the test project are defined here. Other DbContexts, if any, are deleted from the IOC container. So that there will be only DemoContextSqlite and DemoContext classes that we defined in the test project. Migrations for SQLite are run here in the MigrateDbContext method at runtime.
TestBase
For all test fixtures, base class is used. Infrastructure works are taken care of here. For methods that are called once to perform setup before any child tests or methods that immediately before each test are run described here. Also to use client for creating requests are defined in this class.
Up to now we have prepared to arrange and act infrastructures that are needed for test methods. Lets use them in test methods. “Get User Should return valid data” method defined to test GET “api/user” endpoint. First, a user created via repository. Then, get request is sent to endpoint. Since returned response is a json array, its content deserializes into UserDto list. First item of list is assigned to actualUser variable. In assertion, expectedUser should be equal to actualUser.
In the conclusion, writing test for our projects is very important step to prevent errors that we may encounter before going live. In this article, we explained what integration testing is and how you can apply in your projects with examples. If you choose the appropriate test method for your application and use it, you will increase the predictability in your project. By this way many serious bugs can be nipped in the bud if they are detected early enough that may arise in the future. We wish you to encounter fewer mistakes in daily life and to realize them before they explode on your face.
See you in our next articles,
Thanks to Ali Karaca and İncilay Dikbıyık for great contributions.