spring-boottestcontainers

Testcontainers and microservices: how to start a second container that the first is trying to call


I'm writing integration tests using Testcontainers in a Spring Boot application. I am testing the user service, which calls the authentication service in order to encode the password when a user is created, and I'm trying to start two containers:

A postgres:17 database

A custom-built auth-service:latest container

The Postgres container starts correctly, executes init.sql, and works perfectly.

However, the auth-service container fails with this error:

Wait strategy failed. Container exited with code 1
Caused by: java.net.ConnectException: Connection refusedWaiting for URL: http://localhost:56873/auth/health
...
Timed out waiting for URL to be accessible (http://localhost:56873/auth/health should return HTTP [200])


@SpringBootTest(
        properties = {
                "logging.level.org.springframework.security=DEBUG",
        },
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
@TestPropertySource(properties = {
        "spring.jpa.hibernate.ddl-auto=create-drop",
})
class UserControllerIntegrationTests {

    static Network network = Network.newNetwork();

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:17")
            .withDatabaseName("testdb")
            .withUsername("testuser")
            .withPassword("testpass")
            .withNetwork(network)
            .withNetworkAliases("postgres-db");


    @Container
    static GenericContainer<?> authService = new GenericContainer<>("auth-service:latest")
            .withExposedPorts(8080)
            .withNetwork(network)
            .withNetworkAliases("auth-service")
            .waitingFor(Wait.forHttp("/auth/health")
                    .forStatusCode(200)
                    .withStartupTimeout(Duration.ofSeconds(20)));

    static {
        String initScriptPath = "./init.sql";
        try {
            MountableFile.forClasspathResource(initScriptPath);
            System.out.println("✅ Testcontainers: Found init script on classpath: " + initScriptPath);
            postgres.withInitScript(initScriptPath);
        } catch (IllegalArgumentException e) {
            System.err.println("❌ Testcontainers ERROR: Init script NOT found on classpath: " + initScriptPath);
            throw new RuntimeException("Failed to locate database initialization script.", e);
        }
    }

    @Autowired
    private TestRestTemplate restTemplate;

    @Value("${server.api.key}")
    private String apiKey;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private TestHelper httpTestHelper;

    @Autowired
    private JwtService jwtService;

    @Autowired
    private JwtAuthFilter jwtAuthFilter;

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
        registry.add("api.services.auth-service", () ->
                "http://" + authService.getHost() + ":" + authService.getMappedPort(8080));
    }

Solution

  • I'm not sure if it can help you fix your issue but I want to share with you.

    Here are the steps you should follow step by step

    1 ) Define Second Postgres container

    2 ) Env-vars for Auth-Service pointing at that DB

    3 ) Shared Network and withNetworkAliases

    4 ) Increased health‐check timeout & validated path

    5 ) Log dump in @BeforeAll

    6 ) Dynamic property source unchanged except for adding the mapped Auth URL

    Here is the full code shown below

    @SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        properties = {
            "logging.level.org.springframework.security=DEBUG"
        }
    )
    @Testcontainers
    @TestPropertySource(properties = {
        "spring.jpa.hibernate.ddl-auto=create-drop"
    })
    public class UserControllerIntegrationTests {
    
        // Shared Docker network for containers
        static final Network network = Network.newNetwork();
    
        // 1) User Service DB
        @Container
        static PostgreSQLContainer<?> userDb = new PostgreSQLContainer<>("postgres:17")
            .withDatabaseName("testdb")
            .withUsername("testuser")
            .withPassword("testpass")
            .withNetwork(network)
            .withNetworkAliases("postgres-db")
            .withInitScript("init.sql");
    
        // 2) Auth Service DB
        @Container
        static PostgreSQLContainer<?> authDb = new PostgreSQLContainer<>("postgres:17")
            .withDatabaseName("authdb")
            .withUsername("authuser")
            .withPassword("authpass")
            .withNetwork(network)
            .withNetworkAliases("auth-db");
    
        // 3) Auth Service
        @Container
        static GenericContainer<?> authService = new GenericContainer<>("auth-service:latest")
            .withNetwork(network)
            .withNetworkAliases("auth-service")
            .withEnv("SPRING_DATASOURCE_URL", "jdbc:postgresql://auth-db:5432/authdb")
            .withEnv("SPRING_DATASOURCE_USERNAME", "authuser")
            .withEnv("SPRING_DATASOURCE_PASSWORD", "authpass")
            .withExposedPorts(8080)
            .waitingFor(Wait.forHttp("/auth/health").forStatusCode(200).withStartupTimeout(Duration.ofSeconds(60)));
    
        @Autowired
        private TestRestTemplate restTemplate;
    
        @Value("${server.api.key}")
        private String apiKey;
    
        // Add your repositories, services, helpers as needed
        @Autowired
        private UserRepository userRepository;
    
        @Autowired
        private HttpTestHelper httpTestHelper;
    
        @DynamicPropertySource
        static void configureProperties(DynamicPropertyRegistry registry) {
            registry.add("spring.datasource.url", userDb::getJdbcUrl);
            registry.add("spring.datasource.username", userDb::getUsername);
            registry.add("spring.datasource.password", userDb::getPassword);
            registry.add("api.services.auth-service", () ->
                "http://" + authService.getHost() + ":" + authService.getMappedPort(8080)
            );
        }
    
        @BeforeAll
        static void dumpAuthLogs() {
            System.out.println("=== AUTH SERVICE LOGS ===");
            System.out.println(authService.getLogs());
        }
    
        @Test
        void testCreateUserEncodedPassword() {
            // Your test implementation here
        }
    }