2

Written a short convenicence extension for Testcontainers:

fun JdbcDatabaseContainer<*>.execute(query:DSLContext.()-> Query){
    val connection = DriverManager.getConnection(this.getJdbcUrl(),this.getUsername(),this.getPassword())
    val create = DSL.using(connection)
    create.query().execute()
}

And now wanted to test it.

  • Flyway loads 30 entries. These should be visible in allDataPresent
  • canInsert inserts one entry without the extension
  • canInsertWithExtension does the same but via the extension function
  • insertMultipleWithExtension does exactly as its name implies and inserts another 5

All but the allDataPresent testcase (because that one is read-only anyway) are annotated @Transactional.

As such, I'd expect these modifications to be rolled back after the test method.

What instead happens is

[ERROR] Failures: 
[ERROR]   InitDataIT.allDataPresent:70 
Expecting:
 <36>
to be equal to:
 <30>
but was not.
[ERROR]   InitDataIT.canInsert:90 
Expecting:
 <6>
to be equal to:
 <1>
but was not.
[ERROR]   InitDataIT.canInsertWithExtension:112 
Expecting:
 <6>
to be equal to:
 <1>
but was not.

Each @Test is working fine on its own. So the issue must lie with the @Transactional.

So why is that? And more importantly, how do I get the rollbacks?

Full testcase (also tried annotating the class instead, didn't make any difference):

@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = [InitDataIT.TestContextInitializer::class])
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
open class InitDataIT {
    companion object {
        @JvmStatic
        @Container
        private val dbContainer = MySQLContainer<Nothing>().apply {
            withDatabaseName("test")
            withUsername("root")
            withPassword("")
        }
    }
    object TestContextInitializer: ApplicationContextInitializer<ConfigurableApplicationContext> {
        override fun initialize(applicationContext: ConfigurableApplicationContext) {
            TestPropertyValues.of(
                    "spring.datasource.url=${dbContainer.jdbcUrl}",
                    "spring.datasource.username=${dbContainer.username}",
                    "spring.datasource.password=${dbContainer.password}",
                    "spring.datasource.driver-class-name=${dbContainer.driverClassName}"
            ).applyTo(applicationContext)
        }
    }

    private val create:DSLContext


    @Autowired
    constructor(create:DSLContext){
        this.create = create
    }


    @Test
    fun allDataPresent(){
        //given
        val expectedNumberOfEntries = 30

        val query = create.selectCount()
                .from(CUSTOMERS)

        //when
        val numberOfEntries = query.fetchOne{it.value1()}

        //then
        Assertions.assertThat(numberOfEntries).isEqualTo(expectedNumberOfEntries)
    }

    @Test
    @Transactional
    open fun canInsert(){
        //given
        val insertquery = create.insertInto(CUSTOMERS)
                .columns(CUSTOMERS.FIRSTNAME,CUSTOMERS.LASTNAME,CUSTOMERS.EMAIL, CUSTOMERS.STATUS)
                .values("Alice","Tester","[email protected]",CustomerStatus.Contacted.name)

        val expectedNumberInOffice2 = 1

        //when
        insertquery.execute()

        //then
        val numberInOffice2 = create.selectCount()
                .from(CUSTOMERS)
                .where(CUSTOMERS.EMAIL.contains("somewhere"))
                .fetchOne{it.value1()}
        assertThat(numberInOffice2).isEqualTo(expectedNumberInOffice2)

    }

    @Test
    @Transactional
    open fun canInsertWithExtension(){
        //given
        dbContainer.execute {
            insertInto(CUSTOMERS)
                    .columns(CUSTOMERS.FIRSTNAME,CUSTOMERS.LASTNAME,CUSTOMERS.EMAIL, CUSTOMERS.STATUS)
                    .values("Alice","Tester","[email protected]",CustomerStatus.Contacted.name)
        }

        val expectedNumberInOffice2 = 1

        //when
        val numberInOffice2 = create.selectCount()
                .from(CUSTOMERS)
                .where(CUSTOMERS.EMAIL.contains("somewhere"))
                .fetchOne{it.value1()}

        //then
        assertThat(numberInOffice2).isEqualTo(expectedNumberInOffice2)

    }

    @Test
    @Transactional
    open fun insertMultipleWithExtension(){
        //given
        dbContainer.execute {
            insertInto(CUSTOMERS)
                    .columns(CUSTOMERS.FIRSTNAME,CUSTOMERS.LASTNAME,CUSTOMERS.EMAIL, CUSTOMERS.STATUS)
                    .values("Alice","Make","[email protected]", CustomerStatus.Customer.name)
                    .values("Bob","Another","[email protected]", CustomerStatus.ClosedLost.name)
                    .values("Charlie","Integration","[email protected]",CustomerStatus.NotContacted.name)
                    .values("Denise","Test","[email protected]",CustomerStatus.Customer.name)
                    .values("Ellie","Now","[email protected]",CustomerStatus.Contacted.name)
        }

        val expectedNumberInOffice2 = 5

        //when
        val numberInOffice2 = create.selectCount()
                .from(CUSTOMERS)
                .where(CUSTOMERS.EMAIL.contains("somewhere"))
                .fetchOne{it.value1()}

        //then
        assertThat(numberInOffice2).isEqualTo(expectedNumberInOffice2)
    }

}
2

The Spring @Transactional annotation doesn't just magically work with your DriverManager created JDBC connections. Your dbContainer object should operate on your spring managed data source instead.

  • Huh, that would make sense. So if I'd create a helper object for my extension function, into which I inject the Spring-maintained DSLContext, and use that one instead of creating my own from a fresh jdbc connection, it should work? Thank you, will try in a couple hours. – User1291 Apr 12 at 3:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.