Hi, I've upgraded this project from Spring Boot 3.1.4 to 3.2.5.
After that, I've started having issues in my test cases.
I am defining the MyContainers interface
public interface MyContainers {
@Container
@ServiceConnection
PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:16.1");
}
Then, using the ImportTestcontainers annotation
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ImportTestcontainers(MyContainers.class)
class TeamDetailControllerTest implements MyContainers {
...
}
It seems that the test cases are not waiting for the containers to start
2024-04-30T15:20:41.230+02:00 ERROR 72102 --- [spring-data-jpa-relationships] [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource
[org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0: Error creating bean with name 'dataSource' defined in class path resource
[org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Failed to instantiate
[com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception with message: Mapped port can only be obtained after the container is started
If I downgrade to Spring Boot 3.1.4, the test cases will run ok.
How to reproduce
- Clone:
git clone https://github.com/ivangfr/spring-data-jpa-relationships.git - Run:
./mvnw clean test
Thanks
Comment From: wilkinsona
Here's a minimal reproducer:
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.context.ImportTestcontainers;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.instrument.classloading.LoadTimeWeaver;
@SpringBootTest
@ImportTestcontainers(MyContainers.class)
public class LoaderTimeWeaverAwareConsumerImportTestcontainersTests implements MyContainers {
@Test
void test() {
}
@Configuration
@ImportAutoConfiguration(DataSourceAutoConfiguration.class)
static class TestConfiguration {
@Bean
LoaderTimeWeaverAwareConsumer loadTimeWeaverAwareConsumer(JdbcConnectionDetails connectionDetails) {
return new LoaderTimeWeaverAwareConsumer(connectionDetails);
}
}
static class LoaderTimeWeaverAwareConsumer implements LoadTimeWeaverAware {
public LoaderTimeWeaverAwareConsumer(JdbcConnectionDetails jdbcConnectionDetails) {
jdbcConnectionDetails.getJdbcUrl();
}
@Override
public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
}
}
}
interface MyContainers {
@Container
@ServiceConnection
PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:16.1");
}
The problem in the real app is that the entity manager factory implements LoadTimeWeaverAware. This ultimately results in an early call to jdbcConnectionDetails.getJdbcUrl() being made before the container has been started. The involvement of LoadTimeWeaverAware means that it happens before the configuration has been frozen so post-processing hasn't triggered general container initialization. The container bean itself also hasn't been post-processed yet so it's not been started.
Comment From: philwebb
This looks similar to #38913