Почему эти модификации базы данных не отменяются, несмотря на наличие `@ Transactional`? - PullRequest
2 голосов
/ 11 апреля 2019

Написано короткое расширение для удобства 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()
}

А теперь хотел его проверить.

  • Flyway загружает 30 записей.Они должны быть видны в allDataPresent
  • canInsert вставляет одну запись без расширения
  • canInsertWithExtension делает то же самое, но через функцию расширения
  • insertMultipleWithExtension делаетименно так, как следует из его названия, и вставляет еще 5

Все, кроме allDataPresent тестового сценария (потому что он в любом случае доступен только для чтения), аннотируются @Transactional.

. Таким образом,Я ожидаю, что эти модификации будут отменены после метода тестирования.

Вместо этого

[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.

Каждый @Test работает нормально сам по себе.Так что проблема должна быть в @Transactional.

Так почему же это так?И, что еще более важно, как получить откаты?

Полный тестовый сценарий (вместо этого попытался аннотировать класс, без разницы):

@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","Alice.Tester@somewhere.tt",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","Alice.Tester@somewhere.tt",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","Alice.Make@somewhere.tt", CustomerStatus.Customer.name)
                    .values("Bob","Another","Bob.Another@somewhere.tt", CustomerStatus.ClosedLost.name)
                    .values("Charlie","Integration","Charlie.Integration@somewhere.tt",CustomerStatus.NotContacted.name)
                    .values("Denise","Test","Denise.Test@somewhere.tt",CustomerStatus.Customer.name)
                    .values("Ellie","Now","Ellie.Now@somewhere.tt",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)
    }

}

1 Ответ

2 голосов
/ 12 апреля 2019

Аннотация @Transactional Spring не только волшебным образом работает с вашими DriverManager созданными JDBC-соединениями.Вместо этого ваш dbContainer объект должен работать с вашим источником данных, управляемым пружиной.

...