Search code examples
spring-bootspring-data-jpajunit5flywaytestcontainers

Spring Boot Testcontainers PostgreSQL lunches multiple instances


A default version of postgres container is being created & used by spring instead of my own version.

I've created a test:

@SpringBootTest
@ExtendWith(SpringExtension.class)
@AutoConfigureMockMvc
@Testcontainers
class SpringTestJenkinsApplicationTests {


    @Container
    private static final PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:15.2-bullseye")
            .withDatabaseName("postgres")
            .withUsername("root")
            .withPassword("root");


    @DynamicPropertySource
    public static void overrideProps(DynamicPropertyRegistry registry) {
        StringBuilder sb = new StringBuilder(postgreSQLContainer.getJdbcUrl());
        sb.insert(sb.indexOf(":"), ":tc");//.insert(sb.lastIndexOf("://"), ":15.2");
        final String jdbcUrl = sb.toString();
        System.out.println(jdbcUrl);

        registry.add("spring.datasource.url", () -> jdbcUrl);
        registry.add("spring.datasource.username=", postgreSQLContainer::getUsername);
        registry.add("spring.datasource.password=", postgreSQLContainer::getPassword);
        registry.add("spring.flyway.url=", () -> jdbcUrl);
        registry.add("spring.flyway.user=", postgreSQLContainer::getPassword);
        registry.add("spring.flyway.password=", postgreSQLContainer::getUsername);
    }

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void getAllMessages() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/v1/api/message")
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andReturn();
    }

}

Two instances of postgres are lunched the 1st one before spring starts and the 2nd one after.

  1. By me via new PostgreSQLContainer<>("postgres:15.2-bullseye")
  2. By spring via class org.testcontainers.containers.PostgreSQLContainerProvider
@Override
    public JdbcDatabaseContainer newInstance() {
        return newInstance(PostgreSQLContainer.DEFAULT_TAG);
    }

DEFAULT_TAG value in org.testcontainers.containers.PostgreSQLContainer

public static final String DEFAULT_TAG = "9.6.12"

Docker desktop screenshot Docker desktop

test/resources/application.yaml

server:
  port: 8080
spring:
  application:
    name: chat-api
  datasource:
    driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
#    url: jdbc:tc:postgresql:15.2:///postgres
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQL95Dialect
        format_sql: true
    show-sql: false
  flyway:
#    url: jdbc:tc:postgresql:15.2:///postgres
    schemas: public
    enabled: false
    default-schema: public

build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.flywaydb:flyway-core'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    // https://mvnrepository.com/artifact/org.testcontainers/postgresql
    testImplementation 'org.testcontainers:postgresql:1.17.6'
    testImplementation "org.junit.jupiter:junit-jupiter:5.8.1"
    testImplementation "org.testcontainers:testcontainers:1.17.6"
    testImplementation "org.testcontainers:junit-jupiter:1.17.6"

    runtimeOnly 'org.postgresql:postgresql'
}

Parts of the log

12:04:47.710 [Test worker] DEBUG org.testcontainers.utility.ImageNameSubstitutor -- Did not find a substitute image for postgres:15.2-bullseye (using image substitutor: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor'))
12:04:47.711 [Test worker] DEBUG org.testcontainers.images.AbstractImagePullPolicy -- Using locally available and not pulling image: postgres:15.2-bullseye
12:04:47.711 [Test worker] DEBUG 🐳 [postgres:15.2-bullseye] -- Starting container: postgres:15.2-bullseye
12:04:47.711 [Test worker] DEBUG 🐳 [postgres:15.2-bullseye] -- Trying to start container: postgres:15.2-bullseye (attempt 1/1)
12:04:47.711 [Test worker] DEBUG 🐳 [postgres:15.2-bullseye] -- Starting container: postgres:15.2-bullseye
12:04:47.711 [Test worker] INFO 🐳 [postgres:15.2-bullseye] -- Creating container for image: postgres:15.2-bullseye
12:04:47.711 [Test worker] DEBUG org.testcontainers.utility.RegistryAuthLocator -- Looking up auth config for image: postgres:15.2-bullseye at registry: https://index.docker.io/v1/
12:04:47.745 [Test worker] INFO 🐳 [postgres:15.2-bullseye] -- Container postgres:15.2-bullseye is starting: 0e842303d9754829c29fe47662fafad67fcaab76a7e999b32a3bc5cfa4672e0b
12:04:48.896 [Test worker] DEBUG com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.wire -- http-outgoing-1 << "[read] I/O error: null"
12:04:48.896 [Test worker] DEBUG com.github.dockerjava.zerodep.ApacheDockerHttpClientImpl$ApacheResponse -- Failed to close the response
java.nio.channels.ClosedChannelException: null
    at java.base/sun.nio.ch.SocketChannelImpl.ensureOpenAndConnected(SocketChannelImpl.java:215)
    at java.base/sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:403)
    at java.base/sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:59)
    at java.base/sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:107)
    at java.base/sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:101)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.io.LoggingInputStream.read(LoggingInputStream.java:81)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:149)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.io.ChunkedInputStream.getChunkSize(ChunkedInputStream.java:261)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.io.ChunkedInputStream.nextChunk(ChunkedInputStream.java:222)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:147)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.io.ChunkedInputStream.close(ChunkedInputStream.java:314)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.io.Closer.close(Closer.java:48)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.impl.io.IncomingHttpEntity.close(IncomingHttpEntity.java:111)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.io.entity.HttpEntityWrapper.close(HttpEntityWrapper.java:120)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.io.Closer.close(Closer.java:48)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.core5.http.message.BasicClassicHttpResponse.close(BasicClassicHttpResponse.java:93)
    at com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.impl.classic.CloseableHttpResponse.close(CloseableHttpResponse.java:200)
    at com.github.dockerjava.zerodep.ApacheDockerHttpClientImpl$ApacheResponse.close(ApacheDockerHttpClientImpl.java:256)
    at org.testcontainers.shaded.com.github.dockerjava.core.DefaultInvocationBuilder.lambda$null$0(DefaultInvocationBuilder.java:272)
    at com.github.dockerjava.api.async.ResultCallbackTemplate.close(ResultCallbackTemplate.java:77)
    at org.testcontainers.containers.output.FrameConsumerResultCallback.close(FrameConsumerResultCallback.java:99)
    at org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy.waitUntilReady(LogMessageWaitStrategy.java:49)
    at org.testcontainers.containers.wait.strategy.AbstractWaitStrategy.waitUntilReady(AbstractWaitStrategy.java:52)
    at org.testcontainers.containers.PostgreSQLContainer.waitUntilContainerStarted(PostgreSQLContainer.java:143)
    at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:485)
    at org.testcontainers.containers.GenericContainer.lambda$doStart$0(GenericContainer.java:344)
    at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:81)
    at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:334)
    at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:322)
    at org.testcontainers.junit.jupiter.TestcontainersExtension$StoreAdapter.start(TestcontainersExtension.java:242)
    at org.testcontainers.junit.jupiter.TestcontainersExtension$StoreAdapter.access$200(TestcontainersExtension.java:229)
    at org.testcontainers.junit.jupiter.TestcontainersExtension.lambda$null$1(TestcontainersExtension.java:56)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore.lambda$getOrComputeIfAbsent$4(ExtensionValuesStore.java:86)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$MemoizingSupplier.computeValue(ExtensionValuesStore.java:223)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$MemoizingSupplier.get(ExtensionValuesStore.java:211)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$StoredValue.evaluate(ExtensionValuesStore.java:191)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$StoredValue.access$100(ExtensionValuesStore.java:171)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore.getOrComputeIfAbsent(ExtensionValuesStore.java:89)
    at org.junit.jupiter.engine.execution.NamespaceAwareStore.getOrComputeIfAbsent(NamespaceAwareStore.java:53)
    at org.testcontainers.junit.jupiter.TestcontainersExtension.lambda$beforeAll$2(TestcontainersExtension.java:56)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.testcontainers.junit.jupiter.TestcontainersExtension.beforeAll(TestcontainersExtension.java:55)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllCallbacks$12(ClassBasedTestDescriptor.java:395)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllCallbacks(ClassBasedTestDescriptor.java:395)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:211)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:84)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:148)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
    at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
    at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
    at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
12:04:48.897 [Test worker] INFO 🐳 [postgres:15.2-bullseye] -- Container postgres:15.2-bullseye started in PT1.186926S

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.0.5)
jdbc:tc:postgresql://localhost:61527/postgres?loggerLevel=OFF
2023-03-26T12:04:49.067+02:00  INFO 7844 --- [    Test worker] i.z.s.SpringTestJenkinsApplicationTests  : Starting SpringTestJenkinsApplicationTests using Java 17.0.5 with PID 7844 (started by **** in /Users/****/Projects/spring-test-jenkins)
2023-03-26T12:04:49.068+02:00  INFO 7844 --- [    Test worker] i.z.s.SpringTestJenkinsApplicationTests  : No active profile set, falling back to 1 default profile: "default"
2023-03-26T12:04:49.467+02:00  INFO 7844 --- [    Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-03-26T12:04:49.491+02:00  INFO 7844 --- [    Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 20 ms. Found 1 JPA repository interfaces.
2023-03-26T12:04:49.701+02:00  INFO 7844 --- [    Test worker] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-03-26T12:04:49.730+02:00  INFO 7844 --- [    Test worker] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.1.7.Final
2023-03-26T12:04:49.864+02:00  INFO 7844 --- [    Test worker] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2023-03-26T12:04:49.872+02:00  INFO 7844 --- [    Test worker] 🐳 [postgres:9.6.12]                     : Creating container for image: postgres:9.6.12
2023-03-26T12:04:49.935+02:00  INFO 7844 --- [    Test worker] 🐳 [postgres:9.6.12]                     : Container postgres:9.6.12 is starting: 42d8b76bfa5637551090117d1d70a30423386c55c78d95b0d7bb7c8efe169553
2023-03-26T12:04:53.175+02:00  INFO 7844 --- [    Test worker] 🐳 [postgres:9.6.12]                     : Container postgres:9.6.12 started in PT3.302935S
2023-03-26T12:04:53.262+02:00  INFO 7844 --- [    Test worker] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection org.testcontainers.jdbc.ConnectionWrapper@705d914f
2023-03-26T12:04:53.263+02:00  INFO 7844 --- [    Test worker] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.

I've did the following

  • updated docker

  • Changing ddl-auto
spring:
  jpa:
    hibernate:
      ddl-auto: none

to

spring:
  jpa:
    hibernate:
      ddl-auto: create

  • I've managed to change the 2nd container version with
sb.insert(sb.indexOf(":"), ":tc").insert(sb.lastIndexOf("://"), ":15.2");

Which proves its created automatically from the URL.

This fixes the problem for FlyWay version but it seems flyway migrations are applied to a different instance then the one used by spring


  • Setting both the version and ddl-auto to create

Proof 2 processes are created

Flyway: 0f05f92db2137a30b0ff30bbb9150bb357e2f76039d028afade36b9941b81504

Spring 809768dae5a75b89ea0ae28c28141e9e8cde0ed4e070ff342aa712acf551c8bd

``

log

2023-03-27T10:18:39.733+02:00  INFO 56542 --- [    Test worker] i.z.s.SpringTestJenkinsApplicationTests  : Starting SpringTestJenkinsApplicationTests using Java 17.0.5 with PID 56542 (started by **** in /Users/****/Projects/spring-test-jenkins)
2023-03-27T10:18:39.734+02:00  INFO 56542 --- [    Test worker] i.z.s.SpringTestJenkinsApplicationTests  : No active profile set, falling back to 1 default profile: "default"
2023-03-27T10:18:40.135+02:00  INFO 56542 --- [    Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-03-27T10:18:40.160+02:00  INFO 56542 --- [    Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 20 ms. Found 1 JPA repository interfaces.
2023-03-27T10:18:40.377+02:00  INFO 56542 --- [    Test worker] o.f.c.internal.license.VersionPrinter    : Flyway Community Edition 9.5.1 by Redgate
2023-03-27T10:18:40.377+02:00  INFO 56542 --- [    Test worker] o.f.c.internal.license.VersionPrinter    : See what's new here: https://flywaydb.org/documentation/learnmore/releaseNotes#9.5.1
2023-03-27T10:18:40.377+02:00  INFO 56542 --- [    Test worker] o.f.c.internal.license.VersionPrinter    : 
2023-03-27T10:18:40.385+02:00  INFO 56542 --- [    Test worker] 🐳 [postgres:15.2]                       : Creating container for image: postgres:15.2
2023-03-27T10:18:40.452+02:00  INFO 56542 --- [    Test worker] 🐳 [postgres:15.2]                       : Container postgres:15.2 is starting: 0f05f92db2137a30b0ff30bbb9150bb357e2f76039d028afade36b9941b81504
2023-03-27T10:18:41.440+02:00  INFO 56542 --- [    Test worker] 🐳 [postgres:15.2]                       : Container postgres:15.2 started in PT1.055189S
2023-03-27T10:18:41.567+02:00  INFO 56542 --- [    Test worker] o.f.c.i.database.base.BaseDatabaseType   : Database: jdbc:postgresql://localhost:62003/postgres (PostgreSQL 15.2)
2023-03-27T10:18:41.586+02:00  INFO 56542 --- [    Test worker] o.f.core.internal.command.DbValidate     : Successfully validated 1 migration (execution time 00:00.008s)
2023-03-27T10:18:41.596+02:00  INFO 56542 --- [    Test worker] o.f.c.i.s.JdbcTableSchemaHistory         : Creating Schema History table "public"."flyway_schema_history" ...
2023-03-27T10:18:41.643+02:00  INFO 56542 --- [    Test worker] o.f.core.internal.command.DbMigrate      : Current version of schema "public": << Empty Schema >>
2023-03-27T10:18:41.647+02:00  INFO 56542 --- [    Test worker] o.f.core.internal.command.DbMigrate      : Migrating schema "public" to version "1 - Initial"
2023-03-27T10:18:41.662+02:00  INFO 56542 --- [    Test worker] o.f.core.internal.command.DbMigrate      : Successfully applied 1 migration to schema "public", now at version v1 (execution time 00:00.023s)
2023-03-27T10:18:41.848+02:00  INFO 56542 --- [    Test worker] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-03-27T10:18:41.879+02:00  INFO 56542 --- [    Test worker] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.1.7.Final
2023-03-27T10:18:42.021+02:00  INFO 56542 --- [    Test worker] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2023-03-27T10:18:42.027+02:00  INFO 56542 --- [    Test worker] 🐳 [postgres:15.2]                       : Creating container for image: postgres:15.2
2023-03-27T10:18:42.059+02:00  INFO 56542 --- [    Test worker] 🐳 [postgres:15.2]                       : Container postgres:15.2 is starting: 809768dae5a75b89ea0ae28c28141e9e8cde0ed4e070ff342aa712acf551c8bd
2023-03-27T10:18:42.968+02:00  INFO 56542 --- [    Test worker] 🐳 [postgres:15.2]                       : Container postgres:15.2 started in PT0.941469S
2023-03-27T10:18:42.993+02:00  INFO 56542 --- [    Test worker] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection org.testcontainers.jdbc.ConnectionWrapper@35744f8
2023-03-27T10:18:42.994+02:00  INFO 56542 --- [    Test worker] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.

Solution

  • I decide to post this question even tho I found the answer.

    So I was getting started with testcontainers and was following a guide from https://www.baeldung.com/spring-boot-testcontainers-integration-test

    The problem was in the driver if you're using org.testcontainers.jdbc.ContainerDatabaseDriver

    It will expect jdbc:tc<...> in the URL and it will automatically create containers from the URL.

    So I was creating a container with:

    @Container
        private static final PostgreSQLContainer<?> postgreSQLContainer = new PostgreSQLContainer<>("postgres:15.2-bullseye")
                .withDatabaseName("postgres")
                .withUsername("root")
                .withPassword("root");
    

    And with sb.insert(sb.indexOf(":"), ":tc").insert(sb.lastIndexOf("://"), ":15.2");

        @DynamicPropertySource
        public static void overrideProps(DynamicPropertyRegistry registry) {
            StringBuilder sb = new StringBuilder(postgreSQLContainer.getJdbcUrl());
            sb.insert(sb.indexOf(":"), ":tc").insert(sb.lastIndexOf("://"), ":15.2");
            final String jdbcUrl = sb.toString();
            System.out.println(jdbcUrl);
    
            registry.add("spring.datasource.url", () -> jdbcUrl);
            registry.add("spring.datasource.username=", postgreSQLContainer::getUsername);
            registry.add("spring.datasource.password=", postgreSQLContainer::getPassword);
            registry.add("spring.flyway.url=", () -> jdbcUrl);
            registry.add("spring.flyway.user=", postgreSQLContainer::getPassword);
            registry.add("spring.flyway.password=", postgreSQLContainer::getUsername);
        }
    

    This fixed it

    • Change the driver
    spring:
      application:
        name: chat-api
      datasource:
        driver-class-name: org.postgresql.Driver
      #    driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
    
    • Remove tc: from URL since the new driver does not care about it

    Remove line sb.insert(sb.indexOf(":"), ":tc").insert(sb.lastIndexOf("://"), ":15.2");

    And now only one instance will be created & used by both spring & flyway

    Documentation: enter image description here