Подтвердить что ты не робот

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

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

4b9b3361

Ответ 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.