带有 testcontainers 和 jOOQ 的 Spring Boot 不会注入 DSL 上下文

Posted

技术标签:

【中文标题】带有 testcontainers 和 jOOQ 的 Spring Boot 不会注入 DSL 上下文【英文标题】:Spring boot with testcontainers and jOOQ doesn't inject DSL context 【发布时间】:2020-10-17 03:03:54 【问题描述】:

我对 spring boot + jOOQ 和 testcontainers 有一些问题。 DSL 上下文不会注入到我的测试类中。

我为使用SQLContainer做了一些准备

class SpringTestContainer: PostgreSQLContainer<SpringTestContainer> 

    private val postgreSqlPort = 5432
    private val db = "m4"

    companion object 
        var instance: SpringTestContainer? = null

        fun get(): SpringTestContainer 
            if(instance == null) 
                instance = SpringTestContainer()
            

            return instance!!
        
    

    override fun getDatabaseName(): String = db

    constructor() : this("registry.dev.tskad.stdev.ru/m4/db:latest")

    constructor(dockerImageName: String) : super(dockerImageName)
        withImagePullPolicy(PullPolicy.alwaysPull())
        addExposedPort(postgreSqlPort)
        waitStrategy = LogMessageWaitStrategy()
            .withRegEx(".*database system is ready to accept connections.*\\s")
            .withTimes(1)
            .withStartupTimeout(Duration.of(30, ChronoUnit.SECONDS))
    

    override fun getJdbcUrl(): String 
        return String.format("jdbc:postgresql://%s:%d/%s", containerIpAddress, getMappedPort(postgreSqlPort), databaseName)
    

    override fun waitUntilContainerStarted() 
        getWaitStrategy().waitUntilReady(this)
    

    override fun getLivenessCheckPorts(): Set<Int?> 
        return HashSet(getMappedPort(postgreSqlPort))
    


然后我创建了一些抽象来扩展我的集成测试类

@ContextConfiguration(initializers = [SpringIntegrationTest.Initializer::class])
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@JooqTest
abstract class SpringIntegrationTest 

    @get:Rule
    var postgreSQLContainer = SpringTestContainer.get()

    inner class Initializer: ApplicationContextInitializer<ConfigurableApplicationContext> 
        override fun initialize(applicationContext: ConfigurableApplicationContext) 
            with(applicationContext.environment.systemProperties) 
                put("spring.datasource.url", postgreSQLContainer.jdbcUrl)
                put("spring.datasource.username", postgreSQLContainer.username)
                put("spring.datasource.password", postgreSQLContainer.password)
            
        
    


然后我实现了测试类

@ExtendWith(SpringExtension::class)
class TransactionRepositoryImplTest: SpringIntegrationTest() 

    @Autowired
    private var dslContext: DSLContext? = null

    private var transactionRepository: TransactionRepository? = null

    @Before
    fun setUp() 
        assertThat(dslContext).isNotNull
        transactionRepository = TransactionRepositoryImpl(dslContext!!)
    

    @After
    fun tearDown() 

    

    @Test
    fun findTransactionData() 
        transactionRepository?.findTransactionByVehicleUuid(null).apply 
            assertNull(this)
        

    


当我开始这个类的测试时 - 测试失败,因为断言没有通过。 这里是测试报告https://pastebin.com/0HeqDcCT

那么..怎么不可能?我看到了一些关于这个堆栈的指南(Spring/jOOQ/TestContainers)。他们都在工作。也许我错过了一些测试依赖项?如果您有此案例的经验 - 请分享您的解决方案。我将不胜感激。

dependencies 
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("org.springframework.boot:spring-boot-starter-jooq")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.springframework.cloud:spring-cloud-starter-consul-config")
    implementation("org.springframework.cloud:spring-cloud-stream")
    implementation("org.springframework.cloud:spring-cloud-stream-binder-kafka")
    implementation("org.springframework.kafka:spring-kafka")
    implementation("org.springframework.boot:spring-boot-starter-amqp")
    implementation ("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.3")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.10.3")
    runtimeOnly("org.postgresql:postgresql:42.2.12")
    jooqGeneratorRuntime("org.postgresql:postgresql:42.2.12")
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
    testImplementation("org.springframework.boot:spring-boot-starter-test") 
        exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
        exclude(module = "junit")
    
    testImplementation("com.ninja-squad:springmockk:2.0.1")
    testImplementation("org.springframework.cloud:spring-cloud-stream-test-support")
    testImplementation("org.springframework.kafka:spring-kafka-test")
    testImplementation("org.springframework.amqp:spring-rabbit-test")
    testImplementation("org.testcontainers:postgresql:1.14.3")

【问题讨论】:

Seems related 【参考方案1】:

我可能遗漏了一些东西,但在该设置中,您必须手动运行 postgreSQLContainer.start() 某处。例如,它可以在@BeforeAll 中完成。

【讨论】:

很遗憾,没有效果:(【参考方案2】:

我找到了解决方案。正确的方法是覆盖测试容器实现的start 方法,并将凭据放入容器数据库的系统属性中。

这是工作代码:

class SpringTestContainer: PostgreSQLContainer<SpringTestContainer> 

    private val postgreSqlPort = 5432
    private val db = "m4"

    companion object 
        var instance: SpringTestContainer? = null

        fun get(): SpringTestContainer 
            if(instance == null) 
                instance = SpringTestContainer()
            

            return instance!!
        
    

    override fun getDatabaseName(): String = db

    constructor() : this("registry.dev.tskad.stdev.ru/m4/db:latest")

    constructor(dockerImageName: String) : super(dockerImageName)
        withImagePullPolicy(PullPolicy.alwaysPull())
        addExposedPort(postgreSqlPort)
        waitStrategy = LogMessageWaitStrategy()
            .withRegEx(".*database system is ready to accept connections.*\\s")
            .withTimes(1)
            .withStartupTimeout(Duration.of(30, ChronoUnit.SECONDS))
    

    override fun getJdbcUrl(): String 
        return String.format("jdbc:postgresql://%s:%d/%s", containerIpAddress, getMappedPort(postgreSqlPort), databaseName)
    

    override fun waitUntilContainerStarted() 
        getWaitStrategy().waitUntilReady(this)
    

    override fun getLivenessCheckPorts(): Set<Int?> 
        return HashSet(getMappedPort(postgreSqlPort))
    

    override fun start() 
        super.start()
        val container = get()
        System.setProperty("DB_URL", container.jdbcUrl)
        System.setProperty("DB_USERNAME", container.username)
        System.setProperty("DB_PASSWORD", container.password)
    

@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@SpringBootTest(properties = ["spring.cloud.consul.enabled = false"])
@EnableAutoConfiguration(exclude = [
    RabbitAutoConfiguration::class,
    KafkaAutoConfiguration::class
])
class TransactionRepositoryImplTest 

    @get:Rule
    var postgreSQLContainer = SpringTestContainer.get()

    @Autowired
    private lateinit var dslContext: DSLContext

    @Autowired
    private lateinit var transactionRepository: TransactionRepository

    @MockkBean
    private lateinit var connectionFactory: ConnectionFactory // this is the mock for rabbit connection. U may ignore it.

    @Test
    fun contextLoads() 
        Assertions.assertNotNull(dslContext)
        Assertions.assertNotNull(transactionRepository)
    


然后需要修复tests目录下的application.yml

spring:
  datasource:
    platform: postgres
    url: $DB_URL
    username: $DB_USERNAME
    password: $DB_PASSWORD
    driverClassName: org.postgresql.Driver

【讨论】:

以上是关于带有 testcontainers 和 jOOQ 的 Spring Boot 不会注入 DSL 上下文的主要内容,如果未能解决你的问题,请参考以下文章

带有 h2 和 jooq 的 JdbcSQLSyntaxErrorException

带有嵌套列表的 jOOQ 查询

带有 Gradle 和 Kotlin 的 JOOQ 不生成文件

我可以在 JDBC URL 模式下使用带有自定义 Dockerfile 的 Testcontainers 吗?

在 Gitlab-ci 中使用带有 docker-compose 的 Testcontainers 运行端到端测试

SpringBoot 集成测试 Sybase 和 Testcontainers