Gradle Integration Tests
Integration tests are the next step up from unit tests rather than testing classes or functions they verify that the application interacts as expected with the services it depends on.
In this article we’ll add integration tests to a simple Spring Boot app called stampy which keeps track of a stamp collection in a database. The integration test will verify that data is written to a database running in a container.
We’ll be using:
- The JVM Test Suite plugin: Gradle plugin used to define a collection of test suites (Note: This plugin requires Gradle version 7.3+).
- Test Containers: a library to spin up container in tests.
- Assertj-DB: a library providing assertions to test databases.
The complete example can be found on GitHub: https://github.com/howardpaget/stampy
Gradle - JVM Test Suite plugin
The JVM test suite plugin is automatically applied by the java plugin. The configuration below will add:
- A source set
src/integrationTest/javato contain our test code. - A task called
integrationTestwhich runs the tests.
plugins {
id 'java'
...
}
...
testing {
suites {
integrationTest(JvmTestSuite) {
dependencies {
implementation project
}
}
}
}
If your tests use classes from the production code (or production dependencies) you can extend the integrationTest dependency configuration to extend the production dependency configuration like so:
configurations {
integrationTestImplementation.extendsFrom implementation
integrationTestRuntimeOnly.extendsFrom runtimeOnly
}
Optionally you can configure the integrationTest to run as part of a regular build:
tasks.named('check') {
dependsOn testing.suites.integrationTest
}
Test Containers
We’ll use testcontainers to spin up an instance of Postgres. To begin we need to add the required dependencies in the build script:
integrationTestImplementation 'org.testcontainers:postgresql:1.16.3'
integrationTestImplementation 'org.testcontainers:junit-jupiter:1.16.3'
To use the testcontainers we need to:
Annotate the test class with @Testcontainers which activates automatic startup and stop of containers.
Instantiate the container we want to use PostgreSQLContainer and annotate it with @Container to indicate it should be managed by testcontainers. In the example below the container is static which means it’ll be shared by all tests making it non-static will create a new container for ever test,
We also set system properties which are used by Spring Boot to connect the database.
@Testcontainers
public class StampyApplicationTest {
private static final String POSTGRES_IMAGE = "postgres:11.1";
@Container
private static final PostgreSQLContainer postgreSQLContainer =
new PostgreSQLContainer(POSTGRES_IMAGE)
.withDatabaseName("stampydb");
@BeforeAll
static void beforeAll() {
System.setProperty("DB_URL", postgreSQLContainer.getJdbcUrl());
System.setProperty("DB_USERNAME", postgreSQLContainer.getUsername());
System.setProperty("DB_PASSWORD", postgreSQLContainer.getPassword());
}
// the tests...
}
Java Spring Boot Test
We’ll start by adding the following dependencies to our build script:
integrationTestImplementation 'org.springframework.boot:spring-boot-starter-test'
integrationTestImplementation 'org.assertj:assertj-db:2.0.2'
- spring-boot-starter-test:
- Provides
@SpringBootTestwhich spins up a test version of the Spring Boot application and injects components into the test for example theDataSource. - And
@AutoConfigureMockMvcwhich configuresMockMvcan entrypoint to make requests to the web layer.
- Provides
- assertj-db: provides assertions for test data in a database.
In the test below we create a stamp by posting to /stamps then check the stamp is inserted into the stamp table.
@SpringBootTest
@AutoConfigureMockMvc
@Testcontainers
public class StampyApplicationTest {
// setup ...
@Autowired
MockMvc mockMvc;
@Autowired
DataSource dataSource;
@Test
void create_stamp_writes_to_the_stamp_table() throws Exception {
// When: a stamp is created using POST /stamps
mockMvc.perform(post("/stamps")
.contentType(APPLICATION_JSON)
.content("{\\"name\\": \\"DC Collection - Alfred\\"}"));
// Then: the stamp table will contain the new stamp
Table table = new Table(dataSource, "stamp");
assertThat(table)
.hasNumberOfRows(1)
.row(0)
.value("id").isEqualTo(1)
.value("name").isEqualTo("DC Collection - Alfred");
}
// more tests...
}
Further Reading
https://docs.gradle.org/current/userguide/jvm_test_suite_plugin.html