1. введение
Spring обладает отличной поддержкой декларативного управления транзакциями во всем коде приложения, а также в интеграционных тестах .
Однако иногда нам может потребоваться четкий контроль над границами транзакций.
В этой статье мы увидим как программно взаимодействовать с автоматическими транзакциями, настроенными Spring в транзакционных тестах .
2. Предварительные условия
Давайте предположим, что у нас есть некоторые интеграционные тесты в нашем весеннем приложении.
В частности, мы рассматриваем тесты, которые взаимодействуют с базой данных, например, проверяют, правильно ли ведет себя наш уровень персистентности.
Давайте рассмотрим стандартный тестовый класс, аннотированный как транзакционный:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { HibernateConf.class }) @Transactional public class HibernateBootstrapIntegrationTest { ... }
В таком тесте каждый метод тестирования оборачивается транзакцией, которая откатывается при выходе из метода .
Конечно, можно также аннотировать только определенные методы. Все, что мы обсудим в этой статье, относится и к этому сценарию.
3. Класс Тестовой Транзакции
Мы проведем оставшуюся часть статьи, обсуждая один класс: org.springframework.test.context.transaction.TestTransaction .
Это служебный класс с несколькими статическими методами, которые мы можем использовать для взаимодействия с транзакциями в наших тестах.
Каждый метод взаимодействует с единственной текущей транзакцией, которая выполняется во время выполнения тестового метода.
3.1. Проверка состояния Текущей транзакции
Одна вещь, которую мы часто делаем в тестах, – это проверка того, что вещи находятся в том состоянии, в котором они должны быть.
Поэтому мы можем захотеть проверить, существует ли в настоящее время активная транзакция:
assertTrue(TestTransaction.isActive());
Или нам может быть интересно проверить, помечена ли текущая транзакция для отката или нет:
assertTrue(TestTransaction.isFlaggedForRollback());
Если это так, то Spring откатит его непосредственно перед тем, как он закончится, автоматически или программно. В противном случае я совершу его непосредственно перед закрытием.
3.2. Пометка Транзакции для фиксации или отката
Мы можем программно изменить политику фиксации или отката транзакции перед ее закрытием:
TestTransaction.flagForCommit(); TestTransaction.flagForRollback();
Обычно транзакции в тестах помечаются для отката при их запуске. Однако, если метод имеет аннотацию @Commit , вместо этого они начинают помечаться для фиксации:
@Test @Commit public void testFlagForCommit() { assertFalse(TestTransaction.isFlaggedForRollback()); }
Обратите внимание, что эти методы просто помечают транзакцию, как следует из их названий. То есть транзакция не фиксируется и не откатывается немедленно, а только непосредственно перед ее завершением.
3.3. Начало и завершение транзакции
Чтобы зафиксировать или откатить транзакцию, мы либо позволяем методу выйти, либо явно завершаем его:
TestTransaction.end();
Если позже мы захотим снова взаимодействовать с базой данных, нам придется начать новую транзакцию:
TestTransaction.start();
Обратите внимание, что новая транзакция будет помечена для отката (или фиксации) в соответствии с методом по умолчанию. Другими словами, предыдущие вызовы flag For… не оказывают никакого влияния на новые транзакции.
4. Некоторые Детали Реализации
Тестовая транзакция не является чем-то волшебным. Теперь мы рассмотрим его реализацию, чтобы узнать немного больше о транзакциях в тестах с Spring.
Мы видим, что его несколько методов просто получают доступ к текущей транзакции и инкапсулируют некоторые из ее функций.
4.1. Откуда TestTransaction Получает текущую транзакцию?
Давайте сразу перейдем к коду:
TransactionContext transactionContext = TransactionContextHolder.getCurrentTransactionContext();
Держатель TransactionContext – это просто статическая оболочка вокруг ThreadLocal удержание Контекста транзакции .
4.2. Кто устанавливает Локальный контекст потока?
Если мы посмотрим, кто вызывает метод set CurrentTransactionContext , мы обнаружим, что есть только один вызывающий: TransactionalTestExecutionListener.beforeTestMethod .
TransactionalTestExecutionListener – это прослушиватель, который автоматически настраивается на тесты с аннотациями @Transactional .
Обратите внимание, что Контекст транзакции не содержит ссылки на многие фактические транзакции; вместо этого это просто фасад над PlatformTransactionManager .
Да, этот код сильно многоуровневый и абстрактный. Таковы, часто, основные части пружинного каркаса.
Интересно посмотреть, как, несмотря на сложность, Весна не делает никакой черной магии – просто много необходимой бухгалтерии, сантехники, обработки исключений и так далее.
5. Выводы
В этом кратком руководстве мы рассмотрели, как программно взаимодействовать с транзакциями в тестах на основе Spring.
Реализацию всех этих примеров можно найти в проекте GitHub – это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.