Весенние тесты в действии по сервисным транзакциям - PullRequest
2 голосов
/ 22 августа 2011

Я использую @Transactional для моих тестов JUnit (главное преимущество - откат изменений в одном тесте), но у меня есть небольшая проблема, которая влияет на мои транзакции сервиса. Так, например, это

мой сервис:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class ServiceImpl

мой юнит-тест:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-context.xml" })
@Transactional
public class TestService 

@Test
public void testNumberTransaction() {
Entity a = new Entity();
Entity b = new Entity();
service.add(a);
service.add(b);
}

Так наивно я ожидал иметь две отдельные транзакции для service.add (), но если я не использую @nontransactional в методе test, он запускается внутри одной транзакции (но затем он не откатывается после теста).

Ожидается ли это? Могу ли я изменить его с какой-то конфигурацией?

Спасибо

Ответы [ 3 ]

4 голосов
/ 22 августа 2011

Да, это ожидаемо. Propagation.REQUIRED (по умолчанию) означает: выполнить в существующей транзакции, если она существует. Иначе, создайте транзакцию и передайте ее в конце метода.

Так что да, если весь тестовый метод является транзакционным, оба сервисных вызова будут выполняться в контексте тестовой транзакции.

Обратите внимание, что поскольку служба помечена REQUIRED, она должна работать, если транзакция уже существует. Это делает тест действительным: он проверяет ваш сервис в контексте существующей транзакции. Если вы хотите, чтобы служба выполнялась в своей отдельной выделенной транзакции, она должна быть помечена REQUIRES_NEW. Но, конечно, если это так, вы не сможете откатить транзакцию службы, выполнив тест в транзакции.

2 голосов
/ 22 августа 2011

По умолчанию (REQUIRES является поведением распространения по умолчанию), если транзакция уже существует, ваш метод обслуживания не создаст новый, а скорее присоединится к существующему. В вашем случае это транзакция, созданная средой тестирования Spring (которая будет откатана).

Вы можете изменить это поведение, установив распространение на REQUIRES_NEW. Однако, поскольку теперь ваш бизнес add метод выполняется в новой транзакции, после выхода из этого метода транзакция будет зафиксирована, а не откатана. По умолчанию, поскольку все транзакции объединяются в тестовой транзакции JUnit, все изменения, внесенные в базу данных, откатываются.

1 голос
/ 22 августа 2011

@Transactional определяет демаркационную линию.Аннотирование класса эквивалентно аннотированию каждого открытого метода класса, линия разграничения по-прежнему является методом (в вашем случае, testNumberTransaction()).Решение о фиксации / откате будет принято в точке разграничения, то есть при возврате из вашего метода тестирования.Независимо от того, указали ли вы REQUIRES или REQUIRES_NEW распространение, фактическая единица транзакции одна и та же, ваш метод testNumberTransaction(), поэтому два вызова service.add(...) всегда будут выполняться в одной и той же транзакции.

ЕслиВаша цель - всегда откатывать свои транзакции после теста, а затем просто удалить аннотацию @Transactional (или поставить @nontransactional, как вы упомянули).

Если, с другой стороны, вы хотите FORCEновую транзакцию для каждого вызова service.add(...), вы можете создать оболочку для своего класса обслуживания, где у вас есть метод add(...), аннотированный @Transactional (распространением = Propagation.REQUIRES_NEW), и вызвать обернутый service экземпляр add(...) метод оттуда.ИЛИ вы можете добавить декларативное управление транзакциями в ваш весенний тестовый контекст, который добавляет уведомление о транзакции для вашего метода service.add(...).Более подробную информацию о том, как добавить поддержку декларативных транзакций с помощью тегов <tx:XXX>, можно найти в весенней документации .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...