5

I am using a singleton test-container for running multiple integration tests like this :

    @SpringBootTest(webEnvironment = RANDOM_PORT)
    public abstract class BaseIT {
     
      static final PostgreSQLContainer<?> postgreSQLContainer;
     
      static {
        postgreSQLContainer = 
         new PostgreSQLContainer<>(DockerImageName.parse("postgres:13"))
          .withDatabaseName("test")
          .withUsername("duke")
          .withPassword("s3cret")
          .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);
      }
    }

And then extending from the test the base it

    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    class SecondApplicationIT extends BaseIT{
     
      @Autowired
      private TestRestTemplate testRestTemplate;
     
      @Autowired
      private TodoRepository todoRepository;
     
      @AfterEach
      public void cleanup() {
        this.todoRepository.deleteAll();
      }
     
      @Test
      void contextLoads() {
        this.todoRepository.saveAll(List.of(new Todo("Write blog post", LocalDateTime.now().plusDays(2)),
          new Todo("Clean appartment", LocalDateTime.now().plusDays(4))));
     
        ResponseEntity<ArrayNode> result = this.testRestTemplate.getForEntity("/todos", ArrayNode.class);
        assertEquals(200, result.getStatusCodeValue());
        assertTrue(result.getBody().isArray());
        assertEquals(2, result.getBody().size());
      }
     
    }

but now the container is running even after the SecondApplicationIT is finished, how can i stop the container after finishing all test classes which is extending the BaseIt

P.S: i tried with @AfterEach and stopping the container there but it did not work

Arturo Volpe
  • 3,442
  • 3
  • 25
  • 40
Catalina
  • 663
  • 5
  • 20

3 Answers3

2

Solved it by my own, the .withReuse(true); is not needed there in order to allow the ryuk container to start. the ryuk container will then remove all running containers which is ran by the IT.

see: https://engineering.zalando.com/posts/2021/02/integration-tests-with-testcontainers.html

Catalina
  • 663
  • 5
  • 20
1

This can be achieved by following the Singleton Container Pattern without using withReuse(true). No need to call stop() also, since in this case, the container lifetime will be bound to the lifetime of the JVM process (through means of the Ryuk resource reaper mechanism).

Kevin Wittek
  • 1,369
  • 9
  • 26
0

You can use JUnit extension Testcontainers provides.

Mark the class with @Testcontainers, and the container fields with @Container.

Containers in instance fields will be initialized and stopped for every test case. Static fields will be started once for a class and stopped after.

So something like:

@SpringBootTest(webEnvironment = RANDOM_PORT)
@Testcontainers
public abstract class BaseIT {
 
  @Container
  static final PostgreSQLContainer<?> postgreSQLContainer = 
     new PostgreSQLContainer<>(DockerImageName.parse("postgres:13"))
      .withDatabaseName("test")
      .withUsername("duke")
      .withPassword("s3cret");
 
 
  @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);
  }
}

Note that you're also specifying withReuse(true), which can make the container to not be registered for automatic lifecycle management (if the environment you're running tests opts in for that with testcontainers.reuse.enabled = true in ~/.testcontainers.properties)

Oleg Šelajev
  • 3,530
  • 1
  • 17
  • 25
  • The Container annotation does not allow me to share the test container between all integration tests, rather it stops and start the connection again and again and this takes lot of connection limit and resources. I need to stop the container after all tests are finished is it possible? – Catalina May 31 '22 at 22:20
  • You can stop the container at any time by calling `.stop()` on it. After all tests are finished the container will be by default stopped and removed automatically. If either of these doesn't behave as it should it's a bug, please create an issue at https://github.com/testcontainers/testcontainers-java/issues – Oleg Šelajev Jun 01 '22 at 07:23
  • But if i have 5 classes extending the baseIt class, which class should call .stop function in order to stop and remove the test container? – Catalina Jun 01 '22 at 07:30
  • oh, I don't think JUnit provides a way to work with test classes like that. You might be able to build an extension that reacts to https://junit.org/junit5/docs/current/user-guide/#extensions-test-instance-pre-destroy-callback, but I don't know if JUnit even builds the list of all test classes ahead of time so you can be sure no other test class inheriting from the base will be added later. – Oleg Šelajev Jun 01 '22 at 07:53
  • maybe the ryuk container will stop automaticlly but dont know how to use it :/ – Catalina Jun 01 '22 at 08:12
  • Using the JUnit-Jupiter integration adds more complexity as compared to the plain Singleton Container Pattern in this case. – Kevin Wittek Jun 15 '22 at 08:38