Как откатить вложенные транзакции с Propagation.REQUIRES_NEW в интеграционных тестах - PullRequest
20 голосов
/ 03 марта 2011

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

@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 ]

12 голосов
/ 03 марта 2011

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

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

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

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

5 голосов
/ 11 ноября 2011

Я добавил комментарий к 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. Этот SQL «delete» будет запущен в новой транзакции, так как в противном случае его результаты будут регулярно откатываться из-за поведения TransactionConfiguration по умолчанию Spring.

...