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.
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.
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:
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.
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.
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.
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 :).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With