Search code examples
javaspring-bootspring-boot-testtestcontainers

Why does Spring Boot spawn 2 test containers with singleton container approach?


I'm using Spring-boot 2.5.3 with Junit 5

I am trying to test with a PostgresSQL test container. I need to use this same test container with integration test and repository test because it was having two instances in Docker.

Here is my base class where I initialize the container.

import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;

public  class PostgresTestContainer {
    static final PostgreSQLContainer postgreSQLContainer;

    static {
        postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer("postgres:12")
                .withDatabaseName("test")
                .withUsername("postgres")
                .withPassword("postgres")
                .withReuse(true);

        postgreSQLContainer.start();
    }

    @DynamicPropertySource
    static void datasourceConfig(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
        registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
        registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
    }
}

This is repository test.

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class CustomerRepositoryTest extends PostgresTestContainer {

    @Autowired
    private CustomerRepository customerRepository;

    @Test
    @Sql("/scripts/import.sql")
    public void findAllShouldGetAllCustomers() {
        List<Customer> foundCustomers = customerRepository.findAll();
        assertThat(foundCustomers).isNotNull();
        assertThat(foundCustomers.size()).isEqualTo(3);
    }
}

This is another place where I need to use the same test container.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BasicIT extends PostgresTestContainer {
    @Autowired
    private TestRestTemplate testRestTemplate;

    @MockBean
    private CustomerRepository customerRepository;

    @Test
    void shouldFailFetchingCustomersWhenDatabaseIsDown() {
        when(customerRepository.findAll()).thenThrow(new RuntimeException("Cannot connect to database."));
        ResponseEntity<String> result = this.testRestTemplate
                .exchange("/api/v1/customers", HttpMethod.GET, HttpEntity.EMPTY, String.class);
        assertEquals(500, result.getStatusCodeValue());
    }


    @Test
    public void shouldFetchAListOfCustomers(){
        ResponseEntity<List<Customer>> result = this.testRestTemplate
                .exchange("/api/v1/customers", HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference<>() {});
        assertEquals(200, result.getStatusCodeValue());
    }
}

Running:

mvn clean install

This works, and all tests run.

But it opens two containers as shown in Docker image here.

How to use singleton container the correct way? I want to use only one. docker image for 2 test containers


Solution

  • As you can see from the container image names, the containers are different. The second container is your database (postgres), but the first one (ryuk) is an auxillary container, started by the Testcontainer library itself by default. Ryuk performs fail-safe cleanup of other containers (documentation) after they are not needed anymore. There is no need to worry about it, because it's tiny (~5Mb).

    If you absolutelly sure that you don't need it, you can disable ryuk following this instruction.