Spring 使用 testcontainers 启动并且 jOOQ 不注入 DSL 上下文

Spring boot with testcontainers and jOOQ doesn't inject DSL context

我在使用 spring boot + jOOQ 和 testcontainers 时遇到了一些问题。 DSL 上下文不会在我的测试中注入 class.

我为使用做了一些准备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))
    }

}

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

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

}

然后我实施了测试class

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

    }

}

当我开始测试这个 class - 测试失败,因为断言没有通过。 这是测试报告 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")
}

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

中完成

我找到了解决办法。正确的方法是覆盖测试容器实现的 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)
    }

}

然后需要在测试目录

中修复application.yml
spring:
  datasource:
    platform: postgres
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    driverClassName: org.postgresql.Driver