Microservice Solution: Authoring Unit and Integration Tests

Unit and integration tests are essential for ensuring the quality and reliability of your microservice system. In this document, you will learn how to write and run tests for your microservices. Each microservice has its own test projects to validate its functionality and interactions with other services. There are four main types of tests: Entities, Repositories, Application Services, and Controllers.

Prepare the Test Environment

Before writing tests, you need to set up the test environment. Microservice solution templates include MicroServiceNameTestsModule and MicroServiceNameTestBase classes to help you create test classes and run tests. In the module class, you can seed test data, configure the database, and set up the test environment. Test modules are used to register services and configure the test environment, registering the microservice module as AdditionalAssembly to the test module. It doesn't depend on the actual microservice module, because the microservice module has configurations proper for development and production environments. For instance, it uses Redis for distributed caching and RabbitMQ for the distributed event bus. However, in the test environment, it's sufficient to use in-memory cache and in-memory event bus. The base class provides helper methods to access services and perform common test operations. Test projects use the xUnit and Shouldly packages for writing and running tests.

Unit Tests

Unit tests are used to test individual units of code to ensure that they work as expected. In a microservice system, a unit can be a method, a class, or a service. The goal of unit tests is to validate that each unit of the software performs as designed.

Writing Unit Tests

You can run unit tests for your microservice entities to test the domain logic. You can also write unit tests for your application services to test the business logic. However, if you have complex business logic, you may need to write integration tests to test the interaction between different parts of the system. Otherwise, you need to mock the dependencies of the application service to write unit tests.

Here is an example of a unit test for a PermissionDefinitionRecord entity:

public class PermissionDefinitionRecord_Tests
{
    [Fact]
    public void Should_Change_Name()
    {
        // Arrange
        var permission = new PermissionDefinitionRecord(
            Guid.NewGuid(),
            "test",
            "test",
            null,
            "test"
        );
        permission.Name.ShouldBe("test");
        
        // Act
        permission.Patch(new PermissionDefinitionRecord(
            Guid.NewGuid(),
            "test",
            "test2",
            null,
            "test"));
        
        // Assert
        permission.Name.ShouldBe("test2");
    } 
}

In this example, the PermissionDefinitionRecord_Tests class tests the PermissionDefinitionRecord entity by calling its Patch method. The test method Should_Change_Name arranges the test environment, calls the method, and asserts the result. Since it's a unit test, we don't need to inherit from TestBase classes.

Integration Tests

Integration tests are used to test the interaction between different parts of the system to ensure that they work together correctly. You can write integration tests for your repositories, application services, controllers, and other components to validate their interactions.

Writing Integration Tests

Microservice templates provide a test base class for writing integration tests. You can use the test base class to access services and perform common test operations.

Here is an example of an integration test for a repository:

public class PermissionGrantRepository_Tests : AdministrationServiceIntegrationTestBase
{
    private readonly IPermissionGrantRepository _permissionGrantRepository;

    public PermissionGrantRepository_Tests()
    {
        _permissionGrantRepository = GetRequiredService<IPermissionGrantRepository>();
    }
    
    [Fact]
    public async Task Should_Get_Permissions_By_Role_Name()
    {
        var permissionGrants = await _permissionGrantRepository.GetListAsync(RolePermissionValueProvider.ProviderName, "admin");
        
        permissionGrants.ShouldNotBeNull();
        permissionGrants.Count.ShouldBeGreaterThanOrEqualTo(1);
    }
}

In this example, the PermissionGrantRepository_Tests class tests the Should_Get_Permissions_By_Role_Name method of the PermissionGrantRepository by calling its GetListAsync method. To access the services, the test class inherits from the AdministrationServiceIntegrationTestBase class and uses the GetRequiredService method to get the service implementation. Since the administration microservice test module is seeded with test data on application startup, we can directly try to get the admin role's permissions.

Similarly, you can write Application Service test classes to test the business logic of the application services:

public class PermissionAppService_Tests : AdministrationServiceIntegrationTestBase
{
    private readonly IPermissionAppService _permissionAppService;

    public PermissionAppService_Tests()
    {
        _permissionAppService = GetRequiredService<IPermissionAppService>();
    }

    [Fact]
    public async Task Should_Get_Permissions()
    {
        var permissions= await _permissionAppService.GetAsync(RolePermissionValueProvider.ProviderName, "admin");
      
        permissions.ShouldNotBeNull();
        permissions.EntityDisplayName.ShouldBe("admin");
        permissions.Groups.Count.ShouldBeGreaterThanOrEqualTo(1);
        permissions.Groups.SelectMany(x => x.Permissions).Count().ShouldBeGreaterThanOrEqualTo(1);
    }
}

In this example, PermissionAppService_Tests class tests the Should_Get_Permissions method of the PermissionAppService by calling its GetAsync method. The test class inherits from the AdministrationServiceIntegrationTestBase class to access the services.

To test the controllers, you can write test classes for the controllers and test the actions:

public class DemoController_Tests : AdministrationServiceIntegrationTestBase
{
    [Fact]
    public async Task HelloWorld()
    {
        var response = await GetResponseAsStringAsync("/api/administration/demo/hello");
        response.ShouldBe("Hello World!");
    }
}

In this example, DemoController_Tests class tests the HelloWorld action of the DemoController by calling the GetResponseAsStringAsync method to get the response content. The test class inherits from the AdministrationServiceIntegrationTestBase class to access the services and helper methods such as GetResponseAsStringAsync.

Running Tests

You can run your unit or integration tests using the Visual Studio Test Explorer or the dotnet test command. The dotnet test command runs all the tests in the solution.

dotnet test
Was this page helpful?
Please make a selection.
Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document

testtestOtherLiveEvents

19 Jun, 03:00
Online
Watch the Event
Mastering ABP Framework Book
Mastering ABP Framework

This book will help you gain a complete understanding of the framework and modern web application development techniques.

Learn More