Skip to content

Automated testing

PSS®X has been designed with testability in mind. There are some different levels of automated testing;

  • Unit Tests: We typically test a single class (or a very few classes together). These tests will be fast. However, we generally need to deal with mocking for the dependencies of our service(s).
  • Integration Tests: We typically test a service, but this time we don't mock the fundamental infrastructure and services to see if they properly working together.
  • UI (E2E) Tests: We test the UI of the application, just like the users interact with our application.

Unit Tests vs Integration Tests

Integration tests have some significant advantages compared to unit tests

  • Easier to write since we don't work to establish mocking and dealing with the dependencies.
  • Our test code runs with all the real services and infrastructure (including database mapping and queries), so it is much closer to the real application test.

While they have some drawbacks;

  • They are slower compared to unit tests since all the infrastructure is prepared for each test case.
  • A bug in a service may make multiple test cases broken, so it may be harder to find the real problem in some cases.

PSS®X team suggest to go mixed: Write unit or integration test where it is necessary.

Startup Template

Abp web application development framework offers with the test infrastructure properly

The Test Projects

Following solution structure in the Visual Studio:

There are more than one test project, organized by the layers;

  • Domain.Tests is used to test our Domain Layer objects (like Domain Services and Entities).
  • Application.Tests is used to test our Application Layer (like Application Services).
  • EntityFrameworkCore.Tests is used to test our custom repository implementations or EF Core mappings (this project will be different if we use another Database Provider).
  • Web.Tests is used to test the UI Layer (like Pages, Controllers and View Components). This project does exists only for MVC / Razor Page applications.
  • TestBase contains some classes those are shared/used by the other projects.

The Test Infrastructure

The startup solution has the following libraries already installed;

dotnet gitlab ci/cd template provides dotnet test command for pipelines.

This templates provides cobertura and open cover test report formats.

You can create reusable .prop files to add necessary dependencies to your cs project.

<Project>
  <ItemGroup>
    <PackageReference Include="coverlet.collector" Version="X.Y.Z">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="JunitXml.TestLogger" Version="X.Y.Z" />
  </ItemGroup>
</Project>

Replace version numbers with latest available.

In this example the file called test.props is created under the tests folder.

The test.props file can be imported into the .csproj file as shown below.

<Project Sdk="Microsoft.NET.Sdk">
  <Import Project="..\..\common.props" />
  <Import Project="..\test.props" />
  ...
</Project>

The Test Explorer

We can use the Test Explorer to view and run the tests in Visual Studio.

Open the Test Explorer

Open the Test Explorer, under the Tests menu, if it is not already open:

Unit Tests

For Unit Tests, we don't need to much infrastructure. We typically instantiate our class and provide some pre-configured mocked objects to prepare our object to test.

Classes Without Dependencies

In this simplest case, the class we want to test has no dependencies. In this case, we can directly instantiate our class, call its methods and make our assertions.

Classes With Dependencies

If our service has dependencies and we want to unit test this service, we need to mock the dependencies.

Integration Tests

The Integration Test Infrastructure

The Database

The startup template is configured to use in-memory SQLite database for the EF Core (for MongoDB, it uses Mongo2Go library). So, all the configuration and queries are performed against a real database and we can even test database transactions.

Using in-memory SQLite database has two main advantages;

  • It is faster compared to an external DBMS.
  • It create a new fresh database for each test case, so tests doesn't affect each other

The Seed Database

Writing tests against an empty database is not practical. In most cases, we need to some initial data in the database. For example, if we write a test class that query, update and delete the Models, it would be helpful to have a few models in the database before executing the test case.

We can fill it to have an initial data that we can use for each test method.

UI Tests

In general, there are two types of UI Tests;

Non Visual Tests

Such tests completely depends on our UI Framework choice;

  • For an MVC / Razor Pages UI, we typically make request to the server, get some HTML and test if some expected DOM elements exist in the returned result.
  • Angular has its own infrastructure and practices to test the components, views and services.

Visual Tests

Visual Tests are used to interact with the application UI just like a real user does. It fully tests the application, including the visual appearance of the pages and components. There are a lot of tooling in the industry (like Selenium and Test Complete) that we can use to test our application's UI.