Versions Compared

Key

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

Table of Contents

Author:  Oleksandr_Dekin

Introduction

Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.

...

  • Integration test with JUnit 4.12 and Spring Boot < 2.2.6


    Code Block
    languagejava
    titleUsing: JUnit 4.12 and Spring Boot < 2.2.6
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @ContextConfiguration(initializers = IntegrationTest.Initializer.class)
    public class ApplicationIT {
    
        @ClassRule
        public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer().withPassword("inmemory")
                .withUsername("inmemory");
    
        public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
            @Override
            public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
                TestPropertyValues values = TestPropertyValues.of(
                        "spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(),
                        "spring.datasource.password=" + postgreSQLContainer.getPassword(),
                        "spring.datasource.username=" + postgreSQLContainer.getUsername()
                );
                values.applyTo(configurableApplicationContext);
            }
        }
    
        @Ŧest
        public void contextLoads() {
        }
    }


  • Integration test with JUnit 5 and Spring Boot < 2.2.6

    If your application makes use of JUnit 5 but is using a Spring Boot version < 2.2.6, you don't have access to the @DynamicPropertySource feature.
    A possible integration test to verify a REST API endpoint is working as expected looks like the following:

    Code Block
    languagejava
    titleJUnit 5 example with Spring Boot < 2.2.6
    // JUnit 5 example with Spring Boot < 2.2.6
    @Testcontainers
    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    @ContextConfiguration(initializers = DeletePersonIT.Initializer.class)
    public class DeletePersonIT {
     
      @Container
      public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer()
        .withPassword("inmemory")
        .withUsername("inmemory");
     
      @Autowired
      private PersonRepository personRepository;
     
      @Autowired
      public TestRestTemplate testRestTemplate;
     
      public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
     
        @Override
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
          TestPropertyValues values = TestPropertyValues.of(
            "spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(),
            "spring.datasource.password=" + postgreSQLContainer.getPassword(),
            "spring.datasource.username=" + postgreSQLContainer.getUsername()
          );
          values.applyTo(configurableApplicationContext);
        }
      }
     
      @Test
      @Sql("/testdata/FILL_FOUR_PERSONS.sql")
      public void testDeletePerson() {
        testRestTemplate.delete("/api/persons/1");
        assertEquals(3, personRepository.findAll().size());
        assertFalse(personRepository.findAll().contains("Phil"));
     
      }
    }


  • Basic application integration test with JUnit 5 and Spring Boot >= 2.2.6

    If your application uses JUnit 5, you can't use the @ClassRule anymore. Fortunately, Testcontainers provides a solution to write tests with JUnit Jupiter:

    Code Block
    languagejava
    titlejunit-jupiter
    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>${testcontainers.version}</version>
      <scope>test</scope>
    </dependency>

    With this dependency and a more recent version of Spring Boot (> 2.2.6) the basic integration test looks like the following:

    Code Block
    languagejava
    titleJUnit 5 example with Spring Boot >= 2.2.6
    // JUnit 5 example with Spring Boot >= 2.2.6
    @Testcontainers
    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    public class ApplicationIT {
     
      @Container
      public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer()
        .withPassword("inmemory")
        .withUsername("inmemory");
     
      @DynamicPropertySource
      static void postgresqlProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
        registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
        registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
      }
     
      @Test
      public void contextLoads() {
      }
    }


...

  • Now, we can extend and write test:

    Code Block
    languagejava
    titleGuestUserControllerITFooControllerIT
    collapsetrue
    @Log4j2
    class FooControllerIT extends BaseIntegrationTest {
    
      @Test
      void testFoo() throws Exception {
        //...
      }
      
      //...
    }


NOTE: @DirtiesContext - SpringBootTest is reusing Spring context between tests so there is a common Hikari Pool between tests. But in the background testcontainers killed (after the previous test) a container and created a new one (before the next test). SpringBootTest is not aware of that change resulting in a new Postgres container so Hikari Pool is the same as in the previous test (pointing to already used and currently unavailable port). For reusing testcontainers we can add annotation @DirtiesContext

...