Howard Paget
Projects
About

Gradle Integration Tests

Apr 23, 2022
testing

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/java to contain our test code.
  • A task called integrationTest which 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 @SpringBootTest which spins up a test version of the Spring Boot application and injects components into the test for example the DataSource.
    • And @AutoConfigureMockMvc which configures MockMvc an entrypoint to make requests to the web layer.
  • 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