Getting started with Integration Testing in ASP.NET Core

Introduction

It's a type of testing that involves multiple components of the software, from start to finish, and tests them as a group. In the structured development process, integration testing takes modules that have been unit tested as input, groups them into a larger aggregate, and applies integration tests as defined in the test plan, delivering output results that lead to system testing.

Integration testing is important because testing the system with real-time scenarios would give you many insights about your system and how it behaves.

Difference between unit testing and integration testing

In unit testing, we focus on a small piece of code, mostly a single function or method. The goal is to check that a piece of code is working and producing the result as expected. External dependencies like (databases, APIs, or services) are usually mocked to test the logic with dummy data.

Integration testing involves the interaction between different components of a system. Unlike unit tests, integration tests use real or in-memory databases and other services to validate that the integrated components work together in a realistic environment. Let's jump into the code to understand it better.

Setting Up the .NET Core Project for Integration Testing

In this project, we will create a test environment using docker containers to create the test db using Postgres docker image.

Create a test project inside your project solution using the .NET XUnit template.

.NET XUnit

Let's install all the necessary Nuget packages. Below is a list of packages that are required for testing.

  • Microsoft.AspNetCore.Mvc.Testing
  • AutoFixture
  • AutoFixture.AutoMoq
  • Testcontainers
  • Test containers.PostgreSQL

For setting up the test environment we need to use the WebApplicationFactory class provided by Microsoft.Net.

Understanding the WebApplicationFactory

WebApplicationFactory<TEntryPoint> is used to create a TestServer for integration tests. `TEntryPoint` refers to the entry point class of the System Under Test (SUT), which is usually the Program.cs class.

To use this in our testing project, we first need to expose the Program.cs class. The reason for this is to inform the testing project that this is the entry point of the system.

There are two ways to expose the Program.cs class to the testing project.

First, add the below XML in your starting project (WebAPI project).

XML

The second is to create a partial class with the name public partial class Program { }

Partial class

We need to create a customized version of our WebApplicationFactory class to set up the Postgres container for our testing environment. Below is a customized version of the WebApplicationFactory class, which is inherited and also implements the IAsyncLifetime interface.

This implementation ensures that the Postgres container is properly disposed of after our tests have finished running in Visual Studio Test Explorer, releasing all resources acquired by the test env or Docker container.

Docker container

We also need to set up Docker Compose to fetch the latest PostgreSQL image. Right-click on the solution, then choose the "Container Orchestration Support" option and select Docker Compose.

This will automatically add the required files to your solution. Add the below to your docker-compose.yml file.

Container Orchestration Support

Test classes implement a class fixture interface, IClassFixture, to indicate the class contains tests and provide shared object instances across the tests in the class. For that, let's create a BaseIntegrationTest Class and implement IClassFixture<TWebAppFactory>.

After fetching the Postgres image we have to create a database and the required tables inside it to make it a fully functional test environment. Within the constructor of the BaseIntegrationTest class add migration code as well.

Postgres image

Now we are all set with the test environment and let's move to the actual test. Create a test class in my case.

 Create a test class

Now let's run the test from VisualStudio Test Explorer; also, add a docker desktop to your PC. When we run the test first it will fetch the image from the docker hub with the label "postgres: label" and set up the container with our provided configuration in the docker-compose.yml file.

Test Explorer

Docker fetched the PostgreSQL image for us.

 PostgreSQL image 

Here, you can also create containers, and those containers will automatically be disposed of after the test finishes.

Containers

As you can see from the result, our test has also been passed also.

Result

Conclusion

Integration testing ensures that different components of an application work together correctly, validating end-to-end functionality and detecting issues not covered by unit tests.

It is crucial for identifying integration problems and verifying that the system meets its requirements as a whole.

Thanks for reading!