Как откат вложенных транзакций с распространением.Требуется новое в интеграционных тестах

у меня есть несколько интеграционных тестов для различных служб, которые расширяют следующий базовый класс:

@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. Кажется, эти транзакции не откатываются (потому что они являются вложенными транзакциями и, по-видимому, фиксируются внутри "внешней" транзакции?). "Внешняя" транзакция (уровень тестового случая) откатывается, по крайней мере, в соответствии с журналами тестов. Зафиксированные транзакции испортите некоторые более поздние тесты, потому что они изменили тестовые данные.

Я могу обойти это, заставляя тест повторно создавать и повторно заполнять базу данных между тестами, но мой вопрос в том, это ожидаемое поведение или я делаю что-то неправильно в своих тестах? Можно ли принудительно откатить вложенную транзакцию из кода тестирования?

2 ответов


это ожидаемое поведение и является одной из основных причин использования REQUIRES_NEW :

  • иметь возможность откатить новую транзакцию, но зафиксировать внешнюю
  • быть в состоянии совершить новую транзакцию, но откат внешнего

повторное заполнение базы данных между тестами, вероятно, является лучшим решением, и я бы использовал это решение для всех тестов: это позволяет тестам проверять, что все работает правильно, включая фиксацию (которая может не из-за промывки, отложенных ограничений и т. д.).

но если вы действительно хотите откатить транзакцию, решением было бы добавить логический аргумент rollbackAtTheEnd к вашей службе и откат транзакции, если этот аргумент верен.


я добавил комментарий к 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's TransactionConfiguration поведение по умолчанию.