1

My App under test with spring (core) wants to connect to a database by testcontainer, but the testcontainer is not started at this point. How can I achieve that the testcontainer gets ready and then the whole context ist started up?

 import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
 import org.testcontainers.containers.PostgreSQLContainer;
 import org.testcontainers.junit.jupiter.Container;
 import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@SpringJUnitWebConfig(locations = {"classpath:it-context.xml"})
public class IntegrationTest {


    @Container
    private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer();

    @Autowired
    private StatusBean status;

    @Test
    public void appStartedSuccessful(){
        // app & database successful started and connected
        System.out.println(postgresqlContainer.getJdbcUrl());
        System.out.println(postgresqlContainer.getTestQueryString());
        System.out.println(status.getStartupTimestamp());
    }

}
MemLeak
  • 4,456
  • 4
  • 45
  • 84
  • Are you maybe accidentally use `@Test` from JUnit 4? If not, can you add the full stack trace of your test and all imports? Maybe this guide might also help: https://rieckpil.de/howto-write-spring-boot-integration-tests-with-a-real-database/ – rieckpil Aug 07 '20 at 07:46
  • 1
    The only solution was to set the container 'final' and 'static' so database is create once before the tests start. – MemLeak Aug 11 '20 at 11:00

3 Answers3

0

You probably want to wait for the containers to start and be ready

See the documentation though the code seems to indicate that the container should be waiting for a magic log entry, perhaps that's not working for you for some reason.

If you Thread.sleep in your test method, does the Pg container finally arrive as expected? Anything in the container logs to indicate a failure of some sort (unlikely, given the static trick worked for you).

I don't see anything in the JUnit5 integration docs to indicate any manual waiting required when using @Container.

ptomli
  • 11,730
  • 4
  • 40
  • 68
  • the issue is that the spring context is started before the test containers - even with the waiting. trough the annotation magic spring will boot the context and then is missing the database. – MemLeak Aug 11 '20 at 12:20
  • Ah yes, I think that sounds familiar. It could be argued that your application ought to be able to handle a missing db for a short while, perhaps waiting until it's ready before finally proceeding with startup. However, your solution is functional now, which is likely more important. That static startup technique is documented by TestContainers, albeit in a fractionally more complicated mechanism. – ptomli Aug 11 '20 at 13:51
  • In case you use the "static class Initializer" way as mentioned in my post above, the test will wait until db is ready, isn't it? – Arno Feb 04 '22 at 12:44
0

You can write your Datasource Bean class integration tests and create a datasrouce from the parameters from the postgresqlContainer

 @Configuration
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    @Testcontainers    
    public class TestConfiguration{

       PostgreSQLContainer<?> postgres = 
             new PostgreSQLContainer<>(POSTGRES_TEST_IMAGE)
             .withInitScript("somepath/init_postgresql.sql")


            @Primary     
            @Bean     
            public DataSource dataSource(){         
            HikariConfig hikariConfig=new HikariConfig();  
            hikariConfig.setJdbcUrl(postgres.getUrl());    
            hikariConfig.setUsername(postgres.getUsername());  
            hikariConfig.setPassword(postgres.getPassword());   
            hikariConfig.setDriverClassName(postgres.getDriverClassName());         
            HikariDataSource hikariDataSource=new HikariDataSource(hikariConfig);         
            return hikariDataSource;     
            }
    }  

And import the above configuration class into your springboot test, it will automatically override the datasource with TestContainer's Postgres datasoruce.

Your test should look like this

@ExtendWith({SpringExtension.clas})
@SpringBootTest(classes = {DataSourceConfiguration.class, TestConfiguration.class})
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Testcontainers
public class RepoTest {

    @Autowired
    TestRepository testRepository;

    @Test
    public void testRepo() {
        System.out.println("*****************" + testRepository.existsById(1L));
    }

}

For details refer this link : https://medium.com/@arpitsharma14/testcontainer-springboot-tests-faa05b71a7dc

0

In SpringBoot you will have to overwrite the DB parameter using the ones from Postgres-Testcontainer by ApplicationContextInitializer. Testcontainers lifecycle management will handle the rest. Should look somehow like this:

@Testcontainers
@SpringJUnitWebConfig(locations = {"classpath:it-context.xml"})
@ContextConfiguration(initializers = {IntegrationTest.Initializer.class})
public class IntegrationTest {

    @Container
    private static PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer();

    static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
      public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        TestPropertyValues.of(
          "spring.datasource.url=" + postgresqlContainer.getJdbcUrl(),
          "spring.datasource.username=" + postgresqlContainer.getUsername(),
          "spring.datasource.password=" + postgresqlContainer.getPassword()
        ).applyTo(configurableApplicationContext.getEnvironment());
      }
    }

    @Autowired
    private StatusBean status;

    @Test
    public void appStartedSuccessful(){
        // app & database successful started and connected
        System.out.println(postgresqlContainer.getJdbcUrl());
        System.out.println(postgresqlContainer.getTestQueryString());
        System.out.println(status.getStartupTimestamp());
    }

}
Arno
  • 308
  • 3
  • 10