docker-composetestcontainerstestcontainers-junit5

testcontainer initializationError while running a test suite


I have multiple test classes running the same docker-compose with testcontainer.

The suite fails with initializationError although each test passes when performed separately.

Here is the relevant part of the stacktrace occuring during the second test. ./gradlew e2e:test -i

io.foo.e2e.AuthTest > initializationError FAILED
    org.testcontainers.containers.ContainerLaunchException: Container startup failed
        at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:330)
        at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:311)
        at org.testcontainers.containers.DockerComposeContainer.startAmbassadorContainers(DockerComposeContainer.java:331)
        at org.testcontainers.containers.DockerComposeContainer.start(DockerComposeContainer.java:178)
        at io.foo.e2e.bases.BaseE2eTest$Companion.beforeAll$e2e(BaseE2eTest.kt:62)
        at io.foo.e2e.bases.BaseE2eTest.beforeAll$e2e(BaseE2eTest.kt)
       ...

        Caused by:
        org.rnorth.ducttape.RetryCountExceededException: Retry limit hit with exception
            at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:88)
            at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:323)
            ... 83 more

            Caused by:
            org.testcontainers.containers.ContainerLaunchException: Could not create/start container
                at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:497)
                at org.testcontainers.containers.GenericContainer.lambda$doStart$0(GenericContainer.java:325)
                at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:81)
                ... 84 more

                Caused by:
                org.testcontainers.containers.ContainerLaunchException: Aborting attempt to link to container btraq5fzahac_worker_1 as it is not running
                    at org.testcontainers.containers.GenericContainer.applyConfiguration(GenericContainer.java:779)
                    at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:359)
                    ... 86 more

It seems to me that the second test doesn't wait for the first to shutdown previous containers.

Here the base class that all tests inherit from. It is responsible for spinning up the containers.

open class BaseE2eTest {

    ...

    companion object {
        const val A = "containera_1"
        const val B = "containerb_1"
        const val C = "containerc_1"

        val dockerCompose: KDockerComposeContainer by lazy {
            defineDockerCompose()
                .withLocalCompose(true)
                .withExposedService(A, 8080, Wait.forListeningPort())
                .withExposedService(B, 8081)
                .withExposedService(C, 5672, Wait.forListeningPort())
        }

        class KDockerComposeContainer(file: File) : DockerComposeContainer<KDockerComposeContainer>(file)

        private fun defineDockerCompose() = KDockerComposeContainer(File("../docker-compose.yml"))

        @BeforeAll
        @JvmStatic
        internal fun beforeAll() {
            dockerCompose.start()
        }

        @AfterAll
        @JvmStatic
        internal fun afterAll() {
            dockerCompose.stop()
        }
    }
}
docker-compose version 1.27.4, build 40524192
testcontainer 1.15.2
testcontainers:junit-jupiter:1.15.2

Solution

  • After watching this talk, I realized that my testcontainers instantiation approach with Junit5 was wrong.

    Here is the working code:

    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    open class BaseE2eTest {
    
        ...
    
        val A = "containera_1"
        val B = "containerb_1"
        val C = "containerc_1"
    
        val dockerCompose: KDockerComposeContainer by lazy {
            defineDockerCompose()
                .withLocalCompose(true)
                .withExposedService(A, 8080, Wait.forListeningPort())
                .withExposedService(B, 8081)
                .withExposedService(C, 5672, Wait.forListeningPort())
        }
    
        class KDockerComposeContainer(file: File) : DockerComposeContainer<KDockerComposeContainer>(file)
    
        private fun defineDockerCompose() = KDockerComposeContainer(File("../docker-compose.yml"))
    
        @BeforeAll
        fun beforeAll() {
            dockerCompose.start()
        }
    
        @AfterAll
        fun afterAll() {
            dockerCompose.stop()
        }
    }
    

    Now the test suite passes.