Scrobot
Scrobot

Reputation: 1981

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

I have some problem with spring boot + jOOQ and testcontainers. DSL context won't inject in my test class.

I made some preparations for using 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))
    }

}

Then I created some abstraction for extending my integration test 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)
            }
        }
    }

}

and then I implemented test 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)
        }

    }

}

and when I started tests of this class - tests are fails, because of assertions are not passed. Here is the report of tests https://pastebin.com/0HeqDcCT

So.. how it impossible? I saw a few guides with this stack(Spring/jOOQ/TestContainers). And they are all working. Maybe I missed some test dependencies? If you have experience with this case - share your solution, please. I will be very grateful.

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

Upvotes: 2

Views: 1923

Answers (3)

Zhenya
Zhenya

Reputation: 79

in java it helps me:

on test put annotation. See JooqAutoConfiguration.class

@Import({ DataSourceAutoConfiguration.class, 
          TransactionAutoConfiguration.class, JooqAutoConfiguration.class})

Upvotes: 1

Scrobot
Scrobot

Reputation: 1981

I found the solution. The right way was to override the start method of test containers implementation and put in system properties the credentials to container DB.

Here is the working code:

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

}

and then need to fix application.yml in the tests directory

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

Upvotes: 1

Vitaly Chura
Vitaly Chura

Reputation: 860

I might be missing something but in that setup, you have to manually run postgreSQLContainer.start() somewhere. As an example it could be done in @BeforeAll.

Upvotes: 1

Related Questions