如何使用 JUnit 5 在 Kotlin 中创建 TestContainers 基测试类

Posted

技术标签:

【中文标题】如何使用 JUnit 5 在 Kotlin 中创建 TestContainers 基测试类【英文标题】:How to create a TestContainers base test class in Kotlin with JUnit 5 【发布时间】:2019-09-29 15:58:30 【问题描述】:

我正在尝试将 Neo4j TestContainers 与 Kotlin、Spring Data Neo4j、Spring Boot 和 JUnit 5 一起使用。我有很多测试需要使用测试容器。理想情况下,我希望避免在每个测试类中复制容器定义和配置。

目前我有类似的东西:

@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)

如何提取 databaseServer 定义和 @TestConfiguration?我尝试了不同的方法来创建一个基类并让 ContainerTest 扩展它,但它不起作用。据我了解,Kotlin 中不会继承静态属性。

【问题讨论】:

【参考方案1】:

我遇到了同样的问题(让 Spring Boot + Kotlin + Testcontainers 一起工作),在网上搜索了一段时间后(相当)我找到了这个不错的解决方案:https://github.com/larmic/testcontainers-junit5。您只需要将它应用到您的数据库中。

【讨论】:

请在您的回答中包含相关信息;目前,它基本上是一个仅链接的答案,这些都是不受欢迎的,因为(除其他原因外)外部信息会腐烂。 此链接不会失效。 :D【参考方案2】:

下面是我在测试之间共享相同容器的解决方案。

@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)
    
 

这里的关键是不要使用@Container 注解,因为它会在您的第一个测试子类执行所有测试后关闭刚刚创建的容器。 beforeAll() 中的方法 start() 只初始化容器一次(在第一个子类测试执行时),然后在容器运行时什么都不做。

理论上,我们不应该这样做,基于: https://www.testcontainers.org/test_framework_integration/junit_5/

...在所有子类的所有测试都完成之前,不应关闭静态容器,但它不是那样工作的,我不知道为什么。很高兴对此有一些答案:)。

【讨论】:

【参考方案3】:

我在 Kotlin 和 spring boot 2.4.0 中遇到了非常相似的问题。 您可以通过初始化程序实现重用一个测试容器配置的方式,例如: https://dev.to/silaev/the-testcontainers-mongodb-module-and-spring-data-mongodb-in-action-53ng 或 https://nirajsonawane.github.io/2019/12/25/Testcontainers-With-Spring-Boot-For-Integration-Testing/(java 版本) 我还想使用拥有 dynamicProperties 的新方法,它在 java 中是用盒装的。在 Kotlin 中,我做了这样的事情(由于某种原因,我无法使 @Testcontainer 注释工作)。这对我来说不是很优雅但非常简单的解决方案:

MongoContainerConfig 类:

import org.testcontainers.containers.MongoDBContainer

class MongoContainerConfig 
    companion object  
        @JvmStatic
        val mongoDBContainer = MongoDBContainer("mongo:4.4.2")
    

    init 
        mongoDBContainer.start()
    

测试类:

@SpringBootTest(
    classes = [MongoContainerConfig::class]
)
internal class SomeTest 

    companion object 
        @JvmStatic
        @DynamicPropertySource
        fun setProperties(registry: DynamicPropertyRegistry) 
            registry.add("mongodb.uri")  
                  MongoContainerConfig.mongoDBContainer.replicaSetUrl 
        
    

缺点是这个块在每个测试类中都有属性,这表明这里可能需要使用初始化器的方法。

【讨论】:

以上是关于如何使用 JUnit 5 在 Kotlin 中创建 TestContainers 基测试类的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Kotlin 中创建一个打开新活动(Android Studio)的按钮?

如何计算在 Kotlin 中创建的线程数?

如何在 xml 和 kotlin 组合中创建类似布局的计算器

如何在 Android Kotlin 中创建 BottomSheet

如何在 Kotlin 中创建静态函数而不创建对象

如何将 Gradle 中的原生 JUnit 5 支持与 Kotlin DSL 结合使用?