The code shown below works perfectly fine in Spring Boot version 2.7.18. It autoconfigures r2dbc and flyway according to the @DynamicPropertySource such that flyway is able to use jdbc connection to the postgres test container and once the data migrations are complete, the service uses r2dbc connection to the same postgres test container for the tests.
@ActiveProfiles("dbintegrationtest")
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = [ReactiveKotlinServiceOneApplication::class],
)
@Testcontainers
class UserRepositoryImplTest {
companion object : KLogging() {
@Container
@JvmStatic
private val postgreSQLContainer =
PostgreSQLContainer<Nothing>(DockerImageName.parse("postgres:12"))
.withReuse(true) as PostgreSQLContainer<Nothing>
@DynamicPropertySource
@JvmStatic
fun registerDynamicProperties(registry: DynamicPropertyRegistry) {
val host = postgreSQLContainer.host
val port = postgreSQLContainer.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)
val databaseName = postgreSQLContainer.databaseName
val r2dbcUrl = "r2dbc:postgresql://$host:$port/$databaseName?schema=${DatabaseConfig.SCHEMA}"
registry.add("spring.r2dbc.url") { r2dbcUrl }
registry.add("spring.r2dbc.username", postgreSQLContainer::getUsername)
registry.add("spring.r2dbc.password", postgreSQLContainer::getPassword)
registry.add("spring.flyway.enabled") { true }
registry.add("spring.flyway.url", postgreSQLContainer::getJdbcUrl)
registry.add("spring.flyway.user", postgreSQLContainer::getUsername)
registry.add("spring.flyway.password", postgreSQLContainer::getPassword)
}
}
@Autowired
private lateinit var userRepositoryImpl: UserRepositoryImpl
@Autowired
private lateinit var databaseClient: DatabaseClient
private val userId = "user-1"
@BeforeEach
fun `clean up`() {
val numberOfRowsDeleted =
databaseClient.sql("delete from ${DatabaseConfig.SCHEMA}.user")
.fetch().rowsUpdated().block()
logger.info { "before test, the number of rows deleted in 'user' table: $numberOfRowsDeleted" }
}
@Test
fun `insert and get user`() {
val userToBeSaved = User(id = userId, employeeId = 12345)
val userMatchPredicate: (t: User) -> Boolean = { user ->
user.id == userToBeSaved.id && user.employeeId == userToBeSaved.employeeId
}
StepVerifier.create(userRepositoryImpl.updateOrCreate(userToBeSaved))
.expectNextMatches(userMatchPredicate)
.expectComplete()
.verify()
StepVerifier.create(userRepositoryImpl.getById(userToBeSaved.id))
.expectNextMatches(userMatchPredicate)
.expectComplete()
.verify()
}
}
But this functionality stopped working for all Spring Boot versions >= 3.x.x. For example, everything works well for plugin id("org.springframework.boot") version "2.7.18" in build.gradle.kts file. Just by simply changing the plugin to id("org.springframework.boot") version "3.2.4" and rerunning the test causes flyway to throw this error:
> Task :compileTestJava NO-SOURCE
> Task :testClasses UP-TO-DATE
23:05:12.672 [Test worker] INFO org.testcontainers.images.PullPolicy -- Image pull policy will be performed by: DefaultPullPolicy()
23:05:12.673 [Test worker] INFO org.testcontainers.utility.ImageNameSubstitutor -- Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
23:05:12.731 [Test worker] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy -- Loaded org.testcontainers.dockerclient.UnixSocketClientProviderStrategy from ~/.testcontainers.properties, will try it first
23:05:12.831 [Test worker] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy -- Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
23:05:12.831 [Test worker] INFO org.testcontainers.DockerClientFactory -- Docker host IP address is localhost
23:05:12.847 [Test worker] INFO org.testcontainers.DockerClientFactory -- Connected to docker:
Server Version: 24.0.9
API Version: 1.43
Operating System: Ubuntu 23.10
Total Memory: 1899 MB
23:05:12.857 [Test worker] INFO tc.testcontainers/ryuk:0.6.0 -- Creating container for image: testcontainers/ryuk:0.6.0
23:05:12.918 [Test worker] INFO tc.testcontainers/ryuk:0.6.0 -- Container testcontainers/ryuk:0.6.0 is starting: 6e384e775436586857421d733ea1494434476e4fc14db6da5723122fed5fbeb9
23:05:13.089 [Test worker] INFO tc.testcontainers/ryuk:0.6.0 -- Container testcontainers/ryuk:0.6.0 started in PT0.231343S
23:05:13.090 [testcontainers-ryuk] WARN org.testcontainers.utility.RyukResourceReaper -- Can not connect to Ryuk at localhost:33727
java.net.ConnectException: Connection refused
at java.base/sun.nio.ch.Net.pollConnect(Native Method)
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672)
at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:547)
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:602)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
at java.base/java.net.Socket.connect(Socket.java:633)
at org.testcontainers.utility.RyukResourceReaper.lambda$null$1(RyukResourceReaper.java:105)
at org.rnorth.ducttape.ratelimits.RateLimiter.doWhenReady(RateLimiter.java:27)
at org.testcontainers.utility.RyukResourceReaper.lambda$maybeStart$2(RyukResourceReaper.java:101)
at java.base/java.lang.Thread.run(Thread.java:840)
23:05:13.353 [Test worker] INFO org.testcontainers.utility.RyukResourceReaper -- Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
23:05:13.353 [Test worker] INFO org.testcontainers.DockerClientFactory -- Checking the system...
23:05:13.354 [Test worker] INFO org.testcontainers.DockerClientFactory -- ✔︎ Docker server version should be at least 1.6.0
23:05:13.355 [Test worker] INFO tc.postgres:12 -- Creating container for image: postgres:12
23:05:13.357 [Test worker] WARN tc.postgres:12 -- Reuse was requested but the environment does not support the reuse of containers
To enable reuse of containers, you must set 'testcontainers.reuse.enable=true' in a file located at /Users/sakthidharanjagadeesan/.testcontainers.properties
23:05:13.400 [Test worker] INFO tc.postgres:12 -- Container postgres:12 is starting: d05cbb2c74f82725e1d73de942d1e478e650ab7718cc49b9d81b08214333449a
23:05:14.089 [Test worker] INFO tc.postgres:12 -- Container postgres:12 started in PT0.734009S
23:05:14.090 [Test worker] INFO tc.postgres:12 -- Container is started (JDBC URL: jdbc:postgresql://localhost:33728/test?loggerLevel=OFF)
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.4)
2024-04-11T23:05:14.253+05:30 INFO 71909 --- [ Test worker] c.t.t.r.u.a.r.UserRepositoryImplTest : Starting UserRepositoryImplTest using Java 17.0.10 with PID 71909 (started by sakthidharanjagadeesan in /Users/sakthidharanjagadeesan/Documents/source-code/reactive-streaming-distributed-system/kotlin-service-one)
2024-04-11T23:05:14.253+05:30 INFO 71909 --- [ Test worker] c.t.t.r.u.a.r.UserRepositoryImplTest : The following 1 profile is active: "dbintegrationtest"
2024-04-11T23:05:14.468+05:30 INFO 71909 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
2024-04-11T23:05:14.484+05:30 INFO 71909 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 14 ms. Found 0 R2DBC repository interfaces.
2024-04-11T23:05:15.039+05:30 INFO 71909 --- [ Test worker] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 9.22.3 by Redgate
2024-04-11T23:05:15.039+05:30 INFO 71909 --- [ Test worker] o.f.c.internal.license.VersionPrinter : See release notes here: https://rd.gt/416ObMi
2024-04-11T23:05:15.039+05:30 INFO 71909 --- [ Test worker] o.f.c.internal.license.VersionPrinter :
2024-04-11T23:05:15.047+05:30 WARN 71909 --- [ Test worker] onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: 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:33728 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL State : 08001
Error Code : 0
Message : Connection to localhost:33728 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
2024-04-11T23:05:15.056+05:30 INFO 71909 --- [ Test worker] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-04-11T23:05:15.062+05:30 ERROR 71909 --- [ Test worker] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: 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:33728 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL State : 08001
Error Code : 0
Message : Connection to localhost:33728 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.5.jar:6.1.5]
at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66) ~[spring-boot-3.2.4.jar:3.2.4]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.4.jar:3.2.4]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.4.jar:3.2.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[spring-boot-3.2.4.jar:3.2.4]
at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) ~[spring-boot-test-3.2.4.jar:3.2.4]
at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) ~[spring-core-6.1.5.jar:6.1.5]
at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1454) ~[spring-boot-3.2.4.jar:3.2.4]
at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) ~[spring-boot-test-3.2.4.jar:3.2.4]
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) ~[spring-boot-test-3.2.4.jar:3.2.4]
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) ~[spring-boot-test-3.2.4.jar:3.2.4]
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) ~[spring-test-6.1.5.jar:6.1.5]
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) ~[spring-test-6.1.5.jar:6.1.5]
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) ~[spring-test-6.1.5.jar:6.1.5]
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:142) ~[spring-test-6.1.5.jar:6.1.5]
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:98) ~[spring-test-6.1.5.jar:6.1.5]
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) ~[spring-test-6.1.5.jar:6.1.5]
at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) ~[spring-test-6.1.5.jar:6.1.5]
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) ~[na:na]
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) ~[na:na]
at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) ~[na:na]
at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) ~[na:na]
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) ~[na:na]
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at java.base/java.util.Optional.orElseGet(Optional.java:364) ~[na:na]
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) ~[junit-jupiter-engine-5.10.2.jar:5.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ~[na:na]
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ~[na:na]
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) ~[junit-platform-engine-1.10.2.jar:1.10.2]
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) ~[junit-platform-launcher-1.8.2.jar:1.8.2]
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) ~[junit-platform-launcher-1.8.2.jar:1.8.2]
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) ~[junit-platform-launcher-1.8.2.jar:1.8.2]
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) ~[junit-platform-launcher-1.8.2.jar:1.8.2]
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) ~[junit-platform-launcher-1.8.2.jar:1.8.2]
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) ~[junit-platform-launcher-1.8.2.jar:1.8.2]
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) ~[junit-platform-launcher-1.8.2.jar:1.8.2]
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) ~[junit-platform-launcher-1.8.2.jar:1.8.2]
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:119) ~[na:na]
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:94) ~[na:na]
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:89) ~[na:na]
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) ~[na:na]
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) ~[na:na]
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) ~[na:na]
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) ~[na:na]
at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source) ~[na:na]
at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193) ~[na:na]
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129) ~[na:na]
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100) ~[na:na]
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60) ~[na:na]
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) ~[na:na]
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113) ~[na:na]
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65) ~[na:na]
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) ~[gradle-worker.jar:na]
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) ~[gradle-worker.jar:na]
Caused by: org.flywaydb.core.internal.exception.FlywaySqlException: Unable to obtain connection from database: Connection to localhost:33728 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL State : 08001
Error Code : 0
Message : Connection to localhost:33728 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
at org.flywaydb.core.internal.jdbc.JdbcUtils.openConnection(JdbcUtils.java:60) ~[flyway-core-9.22.3.jar:na]
at org.flywaydb.core.internal.jdbc.JdbcConnectionFactory.<init>(JdbcConnectionFactory.java:74) ~[flyway-core-9.22.3.jar:na]
at org.flywaydb.core.FlywayExecutor.execute(FlywayExecutor.java:142) ~[flyway-core-9.22.3.jar:na]
at org.flywaydb.core.Flyway.migrate(Flyway.java:140) ~[flyway-core-9.22.3.jar:na]
at org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:66) ~[spring-boot-autoconfigure-3.2.4.jar:3.2.4]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1833) ~[spring-beans-6.1.5.jar:6.1.5]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782) ~[spring-beans-6.1.5.jar:6.1.5]
... 108 common frames omitted
Caused by: org.postgresql.util.PSQLException: Connection to localhost:33728 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:342) ~[postgresql-42.6.2.jar:42.6.2]
at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:54) ~[postgresql-42.6.2.jar:42.6.2]
at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:263) ~[postgresql-42.6.2.jar:42.6.2]
at org.postgresql.Driver.makeConnection(Driver.java:443) ~[postgresql-42.6.2.jar:42.6.2]
at org.postgresql.Driver.connect(Driver.java:297) ~[postgresql-42.6.2.jar:42.6.2]
at org.springframework.jdbc.datasource.SimpleDriverDataSource.getConnectionFromDriver(SimpleDriverDataSource.java:144) ~[spring-jdbc-6.1.5.jar:6.1.5]
at org.springframework.jdbc.datasource.AbstractDriverBasedDataSource.getConnectionFromDriver(AbstractDriverBasedDataSource.java:205) ~[spring-jdbc-6.1.5.jar:6.1.5]
at org.springframework.jdbc.datasource.AbstractDriverBasedDataSource.getConnection(AbstractDriverBasedDataSource.java:169) ~[spring-jdbc-6.1.5.jar:6.1.5]
at org.flywaydb.core.internal.jdbc.JdbcUtils.openConnection(JdbcUtils.java:48) ~[flyway-core-9.22.3.jar:na]
... 114 common frames omitted
Caused by: java.net.ConnectException: Connection refused
at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:554) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:602) ~[na:na]
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) ~[na:na]
at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na]
at org.postgresql.core.PGStream.createSocket(PGStream.java:243) ~[postgresql-42.6.2.jar:42.6.2]
at org.postgresql.core.PGStream.<init>(PGStream.java:98) ~[postgresql-42.6.2.jar:42.6.2]
at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:132) ~[postgresql-42.6.2.jar:42.6.2]
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:258) ~[postgresql-42.6.2.jar:42.6.2]
... 122 common frames omitted
Comment From: wilkinsona
Can you please share a minimal example that reproduces the behaviour that you have described? Unless the failure is specific to Kotlin, that example should be written in Java in order to minimise the number of moving parts.
Comment From: sakthi-logn
Can you please share a minimal example that reproduces the behaviour that you have described? Unless the failure is specific to Kotlin, that example should be written in Java in order to minimise the number of moving parts.
sure. I will share a minimal example written in Java.
Just reiterating to emphasize that the testcontainers r2dbc test code written in kotlin works well for Spring Boot 2.7.18 but throws flyway connection error for any version of Spring Boot 3.x.x
Comment From: sakthi-logn
This public git repo contains the minimal java code showing the problem with flyway, r2dbc, testcontainers and Spring Boot versions >= 3.0.0.
https://github.com/sakthi-logn/spring-boot-flyway-r2dbc-testcontainer.git
Please do the following steps to reproduce the problem:
1) install docker on your machine because testcontainers needs to connect to the docker daemon.
2) clone the repo
3) change directory to the root of the repo and run ./gradlew clean build test --info to see that there are no errors with Spring boot 2.7.18
4) In the build.gradle.kts file, uncomment the line number 3 which containsid("org.springframework.boot") version "3.2.4"
and comment line number 4 which contains id("org.springframework.boot") version "2.7.18"
5) rerun step 3 to see the same flyway connection error shown in my earlier comments. The only change done was the upgrade to spring boot 3.2.4
I also tried overriding flyway version to the latest using maven BOM overrides in the kotlin gradle.build.kts file but the same error occurred again. So i am not sure if the problem is with flyway.
Please let me know if you need more information.
Comment From: wilkinsona
Thanks for the sample. Unfortunately, it doesn't reproduce the problem with UserRepositoryImplTest passing using Spring Boot 3.2.4:
20:18:39.954 [main] INFO org.testcontainers.images.PullPolicy -- Image pull policy will be performed by: DefaultPullPolicy()
20:18:39.958 [main] INFO org.testcontainers.utility.ImageNameSubstitutor -- Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
20:18:40.145 [main] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy -- Loaded org.testcontainers.dockerclient.UnixSocketClientProviderStrategy from ~/.testcontainers.properties, will try it first
20:18:40.157 [main] WARN org.testcontainers.dockerclient.DockerClientProviderStrategy -- DOCKER_HOST tcp://127.0.0.1:53248 is not listening
20:18:40.362 [main] INFO org.testcontainers.dockerclient.DockerClientProviderStrategy -- Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
20:18:40.364 [main] INFO org.testcontainers.DockerClientFactory -- Docker host IP address is localhost
20:18:40.390 [main] INFO org.testcontainers.DockerClientFactory -- Connected to docker:
Server Version: 20.10.24
API Version: 1.41
Operating System: Docker Desktop
Total Memory: 16010 MB
20:18:40.487 [main] INFO tc.testcontainers/ryuk:0.6.0 -- Creating container for image: testcontainers/ryuk:0.6.0
20:18:41.226 [main] INFO tc.testcontainers/ryuk:0.6.0 -- Container testcontainers/ryuk:0.6.0 is starting: 397df0afad39464ec8a9763eb67c5380da4ab8a5fd197cb0760ffa8433402595
20:18:41.682 [main] INFO tc.testcontainers/ryuk:0.6.0 -- Container testcontainers/ryuk:0.6.0 started in PT1.195098S
20:18:41.687 [main] INFO org.testcontainers.utility.RyukResourceReaper -- Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
20:18:41.687 [main] INFO org.testcontainers.DockerClientFactory -- Checking the system...
20:18:41.687 [main] INFO org.testcontainers.DockerClientFactory -- ✔︎ Docker server version should be at least 1.6.0
20:18:41.688 [main] INFO tc.postgres:12 -- Creating container for image: postgres:12
20:18:41.689 [main] WARN tc.postgres:12 -- Reuse was requested but the environment does not support the reuse of containers
To enable reuse of containers, you must set 'testcontainers.reuse.enable=true' in a file located at /Users/awilkinson/.testcontainers.properties
20:18:41.739 [main] INFO tc.postgres:12 -- Container postgres:12 is starting: f03afe05b62787c5201c871073a37923fd285d3b1a7adc137d4d435590b314c1
20:18:43.477 [main] INFO tc.postgres:12 -- Container postgres:12 started in PT1.789419S
20:18:43.478 [main] INFO tc.postgres:12 -- Container is started (JDBC URL: jdbc:postgresql://localhost:59760/test?loggerLevel=OFF)
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.4)
20:18:43.842 [main] INFO c.t.t.p.p.u.u.a.r.UserRepositoryImplTest - Starting UserRepositoryImplTest using Java 17.0.1 with PID 14001 (started by awilkinson in /Users/awilkinson/dev/temp/spring-boot-flyway-r2dbc-testcontainer)
20:18:43.843 [main] INFO c.t.t.p.p.u.u.a.r.UserRepositoryImplTest - The following 1 profile is active: "local"
20:18:44.277 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
20:18:44.300 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 18 ms. Found 0 R2DBC repository interfaces.
20:18:45.266 [main] INFO o.f.c.i.license.VersionPrinter - Flyway Community Edition 9.22.3 by Redgate
20:18:45.266 [main] INFO o.f.c.i.license.VersionPrinter - See release notes here: https://rd.gt/416ObMi
20:18:45.266 [main] INFO o.f.c.i.license.VersionPrinter -
20:18:45.298 [main] INFO org.flywaydb.core.FlywayExecutor - Database: jdbc:postgresql://localhost:59760/test (PostgreSQL 12.18)
20:18:45.333 [main] INFO o.f.c.internal.database.base.Schema - Creating schema "service_one" ...
20:18:45.340 [main] INFO o.f.c.i.s.JdbcTableSchemaHistory - Creating Schema History table "service_one"."flyway_schema_history" ...
20:18:45.389 [main] INFO o.f.core.internal.command.DbMigrate - Current version of schema "service_one": null
20:18:45.397 [main] INFO o.f.core.internal.command.DbMigrate - Migrating schema "service_one" to version "1.0.0 - create user"
20:18:45.416 [main] INFO o.f.core.internal.command.DbMigrate - Successfully applied 1 migration to schema "service_one", now at version v1.0.0 (execution time 00:00.008s)
20:18:45.569 [main] INFO o.s.b.w.e.netty.NettyWebServer - Netty started on port 59766
20:18:45.576 [main] INFO c.t.t.p.p.u.u.a.r.UserRepositoryImplTest - Started UserRepositoryImplTest in 2.07 seconds (process running for 6.697)
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
20:18:46.496 [main] INFO c.t.t.p.p.u.u.a.r.UserRepositoryImplTest - before test, the number of rows deleted in 'user' table: 0
20:18:46.573 [main] INFO c.t.t.p.p.u.u.a.r.UserRepositoryImplTest - before test, the number of rows deleted in 'user' table: 0
20:18:46.580 [reactor-tcp-nio-6] INFO c.t.t.p.p.u.u.a.r.UserRepositoryImpl - user with id 'user-1' and version '0' does not exist for update
Looking more closely at the failure above, I can see that the Postgres database is available on localhost: 33728:
23:05:14.090 [Test worker] INFO tc.postgres:12 -- Container is started (JDBC URL: jdbc:postgresql://localhost:33728/test?loggerLevel=OFF)
I can also see that Flyway is trying to connect to the same:
Connection to localhost:33728 refused
From Spring Boot's perspective, everything is behaving as it should here with Flyway being configured to use the JDBC URL of the container.
I suspect that there's a problem specific to your environment or Docker networking. The Ryuk problem may also be evidence of this:
23:05:13.090 [testcontainers-ryuk] WARN org.testcontainers.utility.RyukResourceReaper -- Can not connect to Ryuk at localhost:33727
I'm going to close this one, for now at least, as I cannot see any evidence of a problem with Spring Boot. I suspect it's an environmental problem that perhaps only occurs with Spring Boot 3.2's version of Testcontainers.
Comment From: sakthi-logn
Yes. I found out that the problem does not occur in Intel Macbooks but occurred only in Apple M3 laptops. I used colima to install Docker in my apple M3 laptop. Flyway was not able to connect to the docker container runtime inside aarch64 architecture Linux VM started by colima.
So i stopped that VM instance and started a new x86_64 arch Linux VM to run the docker daemon and it worked !! Flyway was able to connect to testcontainer and the tests ran without any failures.
So, the problem seems to have occurred because of the way docker was setup on the apple M3 arm64 arch laptop.