I am trying to do a simple test by calling an endpoint and asserting the response.
It is a Spring Reactive project where I have an r2dbc postgreSQL database.
The test is spinning up a testcontainer with a postgreSQL database.
I can put a breakpoint in the test and access the testcontainer db with PGAdmin and everything looks correctly initialized. I can also boot the application and have a local postgres image running and it works.
Some code to follow along
The repository is implemented as following:
public interface IOrganizerRepository extends ReactiveCrudRepository<Organizer, Long> {}
my application.properties file:
# Server
server.port=8090
# netty
reactor.netty.http.server.accessLogEnabled=true
# Database
spring.r2dbc.url=r2dbc:postgresql://127.0.0.1:8790/organizer_db
spring.r2dbc.username=user
spring.r2dbc.password=password
spring.flyway.user=${spring.r2dbc.username}
spring.flyway.password=${spring.r2dbc.password}
spring.flyway.url=jdbc:postgresql://127.0.0.1:8790/organizer_db
I have a jdbc dependency in the pom.xml to be able to have schema versioning via flyway( which also works when booting and when connecting to testcontainer db with PGAdmin)
Abstract class that spins up the testcontainer(have tried to add the @Container and PostgreSQLContainer inside the test class ) and test class:
public abstract class AbstractIT {
private static final PostgreSQLContainer<?> postgres;
static {
postgres = new PostgreSQLContainer<>(DockerImageName.parse("postgres:14.5"));
postgres.start();
}
@DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("spring.r2dbc.url",
() -> String.format("r2dbc:postgresql://%s:%s/%s",
postgres.getHost(),
postgres.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT),
postgres.getDatabaseName()));
registry.add("spring.r2dbc.username", postgres::getUsername);
registry.add("spring.r2dbc.password", postgres::getPassword);
registry.add("spring.flyway.url", postgres::getJdbcUrl);
}
}
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, classes = OrganizerApplication.class)
@TestPropertySource(locations = "classpath:applicationtest.properties")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringDbTest extends AbstractIT {
@Autowired
private WebTestClient webTestClient;
@Test
void test_test() {
OrganizerDTO org = webTestClient
.get().uri("/v1/admin/organizers/2")
.exchange()
.expectStatus().is2xxSuccessful()
.expectBody(OrganizerDTO.class).returnResult().getResponseBody();
Assertions.assertNotNull(org.id());
System.out.println(org);
Assertions.assertEquals(2L, org.id());
}
}
Error when executing the test:
org.springframework.dao.DataAccessResourceFailureException: Failed to obtain R2DBC Connection; nested exception is io.r2dbc.postgresql.PostgresqlConnectionFactory$PostgresConnectionException: Cannot connect to localhost/<unresolved>:8790
Testcontainer has created a postgres DB with the following properties
url=r2dbc:postgresql://localhost:63696/test # Different port every time
username=test
password=test
I have tested to print the values from application properties by injecting them with @Value and can see that they have been overwritten in runtime
Conclusions
Somehow everything seem to work except that the repository is not connecting to the changed properties from the @DynamicPropertySource and is still trying to connect to the properties set in application.properties. There must be some timing issue when the crud repository initializes the Databaseclient to when the properties are set with the help of DynamicProperties.
I have seen examples using the same set up where it is working, but I might be missing some configuration.
I have been trying to find a way to set the order of initialization of the repo with no luck.
Thanks for the help!
2
Answers
The reason why you are getting
localhost/<unresolved>:8790
which contains the port declared in the properties file and not the random port provided by testcontainers is because thespring.r2dbc.url
property is not being set byAbstractIT
. In order to fix it, move@DynamicPropertySource static void properties
toSpringDbTest
test class.Also, you may not need
@ExtendWith(SpringExtension.class)
if it is already declared by@SpringBootTest
. I recommend to setspring.flyway.user
andspring.flyway.password
in properties method too so you may not needTestPropertySource
andDirtiesContext
I had the same problem as you and I solved it like this
code
add
@ContextConfiguration(initializers = BaseMapperTest.MyTiDBContainer.class)
another way