Как выполнить отложенные транзакции с помощью Propagation.REQUIRES_NEW в тестах интеграции
У меня есть несколько тестов интеграции для различных служб, которые расширяют следующий базовый класс:
@ContextConfiguration(locations="classpath:applicationContext-test.xml")
@TransactionConfiguration(transactionManager="txManager", defaultRollback=true)
@Transactional
public abstract class IntegrationTestBase extends AbstractTransactionalJUnit4SpringContextTests
{
//Some setup, filling test data to a HSQLDB-database etc
}
В большинстве случаев это работает нормально, но у меня есть класс сервиса, который имеет транзакции, определенные с помощью propagation=Propagation.REQUIRES_NEW
. Кажется, что эти транзакции не откатываются (потому что они являются вложенными транзакциями и, по-видимому, совершают транзакции "внешние"?). Операция "внешний" (тестовый уровень) откатывается, по крайней мере, согласно тестовым журналам. Собранные транзакции испортили некоторые более поздние тесты, поскольку они изменили тестовые данные.
Я могу обойти это, заставив тест повторно создать и повторно заполнить базу данных между тестами, но мой вопрос: это ожидаемое поведение, или я что-то не так в моих тестах? Может ли вложенная транзакция принудительно откатиться от тестового кода?
Ответы
Ответ 1
Это ожидаемое поведение и является одной из основных причин использования REQUIRES_NEW:
- возможность отката новой транзакции, но совершить внешний
- возможность совершить новую транзакцию, но откат внешнего
повторное заполнение базы данных между тестами, вероятно, является лучшим решением, и я бы использовал это решение для всех тестов: это позволяет проверять, что все работает правильно, включая фиксацию (которая может завершиться неудачей из-за сброса, отложенных ограничений и т.д.).
Но вы действительно хотите отменить транзакцию, решением было бы добавить логический аргумент rollbackAtTheEnd
к вашей службе и отменить транзакцию, если этот аргумент верен.
Ответ 2
Я добавил комментарий к Spring билет улучшения. Я также скопирую его здесь:
Я работал над этой проблемой, преобразовывая все методы обслуживания, которые были декларативно настроены следующим образом
@Transactional(propagation = REQUIRES_NEW)
public Object doSmth() {
// doSmthThatRequiresNewTx
}
вместо TransactionTemplate
:
private TransactionTemplate transactionTemplate;
public Object doSmth() {
return transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
// doSmthThatRequiresNewTx
}
});
}
В рамках тестов я настраиваю поведение TransactionTemplate
для распространения PROPAGATION_REQUIRED
, в реальном приложении я настраиваю поведение распространения TransactionTemplate
как PROPAGATION_REQUIRES_NEW
. Он работает так, как ожидалось. Ограничение этого обходного пути заключается в том, что в тестах невозможно утверждать, что внутренняя транзакция не отката в исключительном сценарии.
Другим решением было бы явно удалить все doSmth()
в базе данных в методе @AfterTransaction
в тесте. Этот "delete" SQL будет запущен в новой транзакции, так как его результаты будут в противном случае откатываться в обычном режиме по умолчанию Spring TransactionConfiguration
.