Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table Of Contents

Table of Contents

AddressesRelated Jiras:

  • Jira Legacy
    serverSystem Jira
    serverId4733707d-2057-3a0f-ae5e-4fd8aff50176
    keyCPS-95
  • Jira Legacy
    serverSystem Jira
    serverId4733707d-2057-3a0f-ae5e-4fd8aff50176
    keyCPS-124
  • Jira Legacy
    serverSystem Jira
    serverId4733707d-2057-3a0f-ae5e-4fd8aff50176
    keyCPS-128

Overview

The CPS-RI module which is responsible for data persistence and retrieval from the database is based on
Spring on Spring Data framework.
Following components are used:

...

In order to reach the desirable level of reliability it's expected the functionality to be covered with tests.

Below is comparison between unit test vs integration test approaches.

Drawio
borderfalse
diagramNameCPS DB Testing Unit vs Integration
simpleViewerfalse
widthlinksauto
tbstyleinline
lboxfalse
diagramDisplayNamediagramWidth851
revision7

While using unit tests is a common approach for Java application testing, it seems insufficient  in a context of testing the
actual data persistence/retrieval functionality:

...

  • All levels of abstraction are involved in tests, it means all of them are impacting on test results same way as within
    a deployed application
  • JPA repositories are subjects of test
  • Complete validation of expected behavior, including database schema initialization
  • Can be used on build, keeping desired level of reliability and consistency
  • Tests can be used on development stage making the database deployment (on dev environment) optional

Components

Components used to serve the integration tests are shown on diagram below.

Drawio
diagramDisplayName
borderfalse
diagramNameCPS DB Testing components diagram
simpleViewerfalse
widthlinksauto
tbstyleinline
lboxfalse
diagramWidth721781
revision1

Test Containers

Test container life circle management

Database initialization notes

@SpringBootTest

Test template

...

4

  • Docker provides a container with running database instance which is used during the test
  • Test container component communicates with Docker via CLI (or Engine API depending on Docker deployment),
    manages required container to be created/started before test and stopped/removed after the test is completed
  • Test container component is used by JUnit test as a Class Rule 
  • The connection to test database is served using standard Spring framework components 

More details on implementation are below.


Test Containers

Running instance of database for integration test is provided by TestContainers Java library component. 

Docker availability is a prerequisite.

Test container life circle management

Test Containers library is integrated with JUnit via Rule interface implementation:

  • On start() method invocation the requested test container is created (if absent) within a Docker and started (if not running yet)
  • On stop() method invocation the test container is stopped and removed

If annotated as @Rule the test container creation/start then stop/removal operations will be performed for each test method.
If annotated as @ClassRule these operation will be performed for each test class.

In order to save the resources (and all tests execution time as result) the default TestContainer implementation was wrapped to custom
(singleton type of) artifact. This way the database test container is initialized only once for all the tests. The test container removal is
linked to JVM shutdown event to avoid this container remain running in docker.

Drawio
borderfalse
diagramNameCPS Test Containers life circle mgmt diagram
simpleViewerfalse
linksauto
tbstyleinline
lboxfalse
diagramWidth761
revision2


NB. The default remaining test containers removal (on JVM termination) was served by dedicated RYUK container (part of TestContainers library).
However this container requires running as privileged one, so it was disabled in order make TestContainers available on Jenkins. It made it
necessary to explicitly invoke test container removal logic on JVM termination event.


@SpringBootTest

Using @SpringBootTest annotation for tests allows @Autowired Spring components (including JPA repository instances) initialization for testing
same way as it's done on regular Spring Boot application execution.

To be used the @SpringBootApplication annotated class is required. However both application class  and associated configuration are allocated
in CPS-REST maven module which cannot be accessed from CPS-RI module on build (when the tests classes are compiled and executed).

So in order to fulfill the @SpringBootTest requirements and to properly initialize DataSource object the substitutions for both Spring Boot application
class and configuration (DataSource properties) are set directly within CPS-RI module (test scope).

Drawio
borderfalse
diagramNameCPS Spring boot RI dependencies on build
simpleViewerfalse
width
linksauto
tbstyleinline
lboxfalse
diagramDisplayName
diagramWidth651
revision3

NB. There is no need to initialize the Spring Boot application within CPS-RI in same scale as it's done for CPS-REST module, so the 
application.yaml configuration within CPS-RI module contains mainly database related parameters. In case these parameters are updated
within primary configuration file same changes require to be provided to test configuration file as well.

Database initialization

The datasource is configured in application.yml test configuration file like below

Code Block
languageyml
spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    driverClassName: org.postgresql.Driver
    initialization-mode: always

The database connection parameters are expected to be defined using environment variables.

Due to  ports exposed for a test containers (part of DB_URL) are variable (to allow concurrent tests executions and to avoid conflicts with
existing containers) the expected variables are set after the test container being initialized directly from TestContainer wrapper artifact

Code Block
languagejava
titleDatabaseTestContainer.java
    @Override
    public void start() {
        super.start();
        System.setProperty("DB_URL", databaseTestContainer.getJdbcUrl());
        System.setProperty("DB_USERNAME", databaseTestContainer.getUsername());
        System.setProperty("DB_PASSWORD", databaseTestContainer.getPassword());
    }


The database schema initialization is performed by Spring framework (same way for both runtime and testing) using schema.sql file.
See https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-database-initialization

Test data

Spring framework allows data setting for testing using @Sql annotation.

Single or multiple SQL scripts can be defined to be executed before the test method (resources folder is a root).

Code Block
languagejava
    @Test
	@Sql({"/data/clear.sql", "/data/test-data.sql"})
    public void test() {
		// test 
    }

See https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testcontext-executing-sql-declaratively

Note. During 2023 CPS moved away from using SQL scripts to insert Test Data. Instead we insert dat programmatically during the test for more details see CPS (semi-) Integration Tests

Resources

Frameworks and libraries:

Tutorials:

...