Изменения данных, совершенные вложенной внутренней транзакцией, действительно должны быть немедленно видны в родительской транзакции.
И я действительно не знаю, почему они не в транзакционном контексте GroovyTestCase
. Другие тоже не знают и используют аналогичные подходы к моим .
Рассмотрим следующий контрольный пример. Сам тестовый случай не транзакционный, но вызывает транзакционный метод. - Это работает, как ожидалось.
class TransactionalMethodTest extends GroovyTestCase {
static transactional = false // test case is not transactional
def customerService
void testTransactionsCommit() {
// start a new transaction,
// setting order 1 inactive
setOrderInactive()
assert ! Order.get(1).isActive
}
@Transactional(propagation = Propagation.REQUIRED)
private void setOrderInactive() {
// make sure that order 1 is active
Order order = Order.get(1)
order.isActive = true
order.save(flush:true)
assert Order.get(1).isActive
// the following method acts in isolation level
// Propagation.REQUIRES_NEW, which means,
// a new, nested, transaction is started
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
customerService.cancelOrders([1])
// changes from the nested transaction are
// visible, instantly
assert ! Order.get(1).isActive
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
}
Теперь рассмотрим следующий, "нормальный", транзакционный, контрольный пример. Изменения данных из вложенной транзакции не видны в родительской транзакции.
Все, что я могу сказать, это то, что транзакционные тестовые примеры не работают с вложенными транзакциями, поэтому используйте нетранзакционный тестовый пример выше .
Если мы не понимаем причину, мы можем, по крайней мере, знать наши варианты.
class TransactionalTestCaseTests extends GroovyTestCase {
static transactional = true // default; Propagation.REQUIRED
def customerService
void testTransactionsCommit() {
// make sure that order 1 is active
Order order = Order.get(1)
order.isActive = true
order.save(flush:true)
assert Order.get(1).isActive
// the following method acts in isolation level
// Propagation.REQUIRES_NEW, which means,
// a new, nested, transaction is started
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
customerService.cancelOrders([1])
// the changes from the inner transaction
// are not yet visible
assert Order.get(1).isActive
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
@Override
protected void tearDown() throws Exception {
// the changes from the inner transaction
// are still not visible
assert Order.get(1).isActive
super.tearDown();
}
}
Не связанный с вашим основным вопросом, но с вашими общими намерениями, вот тестовый пример, который проверяет, правильно ли откатана вложенная транзакция:
class NestedTransactionRolledBackTests extends GroovyTestCase {
static transactional = false // test case is not transactional
def customerService
void testTransactionsCommit() {
// start a new transaction,
// setting order 1 active
setOrderActive()
assert Order.get(1).isActive
}
@Transactional(propagation = Propagation.REQUIRED)
private void setOrderActive() {
// make sure that order 1 is active
Order order = Order.get(1)
order.isActive = true
order.save(flush:true)
assert Order.get(1).isActive
// the following method acts in isolation level
// Propagation.REQUIRES_NEW, which means,
// a new, nested, transaction is started.
// This transaction will fail, and be rolled back.
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
shouldFail(NullPointerException) {
customerService.cancelOrders([1, -999])
}
// changes from the nested transaction are
// visible, instantly.
// The changes have been rolled back
assert Order.get(1).isActive
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
}
Наконец, некоторые более общие sidenotes, это не boolean transactional = true
(что, похоже, работает), но static transactional = true
. Ваши интеграционные тесты также должны extend
GroovyTestCase
, а не его подкласс GrailsUnitTestCase
, так как вам не нужны его возможности для насмешек. Поле isActive
должно быть названо active
, тогда получатель isActive()
будет автоматически создан в соответствии с соглашением об именах.