Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a TestContainers base test class in Kotlin with JUnit 5

I am trying to use Neo4j TestContainers with Kotlin, Spring Data Neo4j, Spring Boot and JUnit 5. I have a lot of tests that require to use the test container. Ideally, I would like to avoid copying the container definition and configuration in each test class.

Currently I have something like:

@Testcontainers
@DataNeo4jTest
@Import(Neo4jConfiguration::class, Neo4jTestConfiguration::class)
class ContainerTest(@Autowired private val repository: XYZRepository) {

    companion object {
        const val IMAGE_NAME = "neo4j"
        const val TAG_NAME = "3.5.5"

        @Container
        @JvmStatic
        val databaseServer: KtNeo4jContainer = KtNeo4jContainer("$IMAGE_NAME:$TAG_NAME")
                .withoutAuthentication()
    }

    @TestConfiguration
    internal class Config {
        @Bean
        fun configuration(): Configuration = Configuration.Builder()
                .uri(databaseServer.getBoltUrl())
                .build()
    }

    @Test
    @DisplayName("Create xyz")
    fun testCreateXYZ() {
        // ...
    }

}

class KtNeo4jContainer(val imageName: String) : Neo4jContainer<KtNeo4jContainer>(imageName)

How can I extract the databaseServer definition and the @TestConfiguration? I tried different ways of creating a base class and having the ContainerTest extend it, but it is not working. From what I understand, static attriubutes are not inherited in Kotlin.

like image 659
Martin L. Avatar asked May 12 '19 13:05

Martin L.


People also ask

How do I write a JUnit 5 Test in Kotlin?

At its very simplest, a JUnit 5 test written in Kotlin works exactly as would be expected. We write a test class, annotate our test methods with the @Test annotation, write our code, and perform the assertions: Everything here just works out of the box.

How do I add testcontainers as a test-scoped dependency?

Add Testcontainers as a test-scoped dependency First, add Testcontainers as a dependency as follows: 2. Get Testcontainers to run a Redis container during our tests First, you'll need to annotate the test class with @Testcontainers. Furthermore, add the following to the body of our test class:

What is the dependency of JUnit Jupiter on testcontainers core?

Since this module has a dependency onto JUnit Jupiter and on Testcontainers core, which has a dependency onto JUnit 4.x, projects using this module will end up with both, JUnit Jupiter and JUnit 4.x in the test classpath.

How do I run a Redis container during a JUnit test?

Get Testcontainers to run a Redis container during our tests First, you'll need to annotate the test class with @Testcontainers. Furthermore, add the following to the body of our test class: The @Container annotation tells JUnit to notify this field about various events in the test lifecycle.


2 Answers

I've had the same issue (making Spring Boot + Kotlin + Testcontainers work together) and after searching the web for (quite) a while I found this nice solution: https://github.com/larmic/testcontainers-junit5. You'll just have to adopt it to your database.

like image 140
Daniel Bimschas Avatar answered Dec 21 '22 17:12

Daniel Bimschas


Below my solution for sharing same container between tests.

@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
abstract class IntegrationTest {

companion object {
    @JvmStatic
    private  val mongoDBContainer = MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))
        .waitingFor(HostPortWaitStrategy())

    @BeforeAll
    @JvmStatic
    fun beforeAll() {
        mongoDBContainer.start()
    }

    @JvmStatic
    @DynamicPropertySource
    fun registerDynamicProperties(registry: DynamicPropertyRegistry) {
        registry.add("spring.data.mongodb.host", mongoDBContainer::getHost)
        registry.add("spring.data.mongodb.port", mongoDBContainer::getFirstMappedPort)
    }
 }
}

The key here is to not use @Container annotation as it will close just created container after your first test subclass executes all tests. Method start() in beforeAll() initialize container only once (upon first subclass test execution), then does nothing while container is running.

By theory we shouldn't have to do this hack, based on: https://www.testcontainers.org/test_framework_integration/junit_5/

...container that is static should not be closed until all of tests of all subclasses are finished, but it's not working that way and I don't know why. Would be nice to have some answer on that :).

like image 40
tymoteuszauke Avatar answered Dec 21 '22 15:12

tymoteuszauke