I am currently developing a Spring Boot application where I want to use Testcontainers. Everything works fine, but there’s an issue preventing the application from starting.
The error message says:
Message: Connection to localhost:32868 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
I’m using Flyway, and it seems that the main problem is related to initializing the Flyway bean:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Failed to initialize dependency 'flywayInitializer' of LoadTimeWeaverAware bean 'entityManagerFactory': Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Unable to obtain connection from database: Connection to localhost:32868 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
My tests look like this:
@Testcontainers
@SpringBootTest
@AutoConfigureMockMvc
public class OwnerRestControllerTest {
// Ideally, the container should be recognized as "in use"
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgresContainer =
new PostgreSQLContainer<>(DockerImageName.parse("postgres:17-alpine"));
@Autowired
private MockMvc mockMvc;
@Autowired
private OwnerRepository ownerRepository;
@Test
@Sql(scripts = "classpath:insert-owners.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:delete-owners.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void getAll() throws Exception {
mockMvc.perform(get("/rest/owners")
.param("lastNameContains", "Doe"))
.andExpect(status()
.isOk())
.andExpect(jsonPath("$.content[*].lastName").value(everyItem(is("Doe"))))
.andExpect(jsonPath("$.content.length()").value(2));
}
}
I found a workaround, but I’m not happy with it. It involves simply waiting a bit longer:
@Testcontainers
@SpringBootTest
@AutoConfigureMockMvc
public class OwnerRestControllerTest {
@BeforeAll
static void beforeAll() throws InterruptedException {
Thread.sleep(5000);
}
}
Has anyone faced a similar issue? For those interested, I’ve created a repository.
You can run the application from this branch: GitHub Repository
Is there a different approach I should use for configuring the environment with Testcontainers and Flyway? Or is this simply a bug?
I downloaded your code and tried to replicate your error (without having the @BeforeAll) and could not get the same result (image). Anyway, if you still get the same problem and you don't like the solution with the Thread.sleep()
, you could use the annotation @DynamicPropertySource
.
@DynamicPropertySource is a method-level annotation that you can use to register dynamic properties to be added to the set of PropertySources in the Environment for an ApplicationContext loaded for an integration test. Dynamic properties are useful when you do not know the value of the properties upfront – for example, if the properties are managed by an external resource such as for a container managed by the Testcontainers project.
I think the problem you are finding is that your application tries to connect to PostgreSQL database before is available (Unable to obtain connection from database: Connection to localhost:32868 refused
). With @DynamicPropertySource
you can test your Spring component even if it depends on something like a database.
You can do the following:
@Container
static PostgreSQLContainer<?> postgresContainer =
new PostgreSQLContainer<>("postgres:17-alpine")
.withDatabaseName("prop")
.withUsername("postgres")
.withPassword("pass");
@DynamicPropertySource
static void registerPgProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgresContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgresContainer::getUsername);
registry.add("spring.datasource.password", postgresContainer::getPassword);
}
For the code I've been guided by this page.
Note: In that same page I've also found that you can use as an alternative Text Fixtures
. I tried that solution as well, but in that case it was giving me the same problem of refused connection. You can try to implement that alternative, see if you have better luck.