2016-04-10 3 views
2

Я пытался проверить @TransactionalEvents (особенность Spring 4.2 https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2) с нашими существующими Spring JUnit тестов (запустить либо через @TransactionalTestExecutionListener или подклассов AbstractTransactionalUnit4SpringContextTests, но, похоже, есть вынужденный выбор - - либо запустить тест без @Rollback аннотацию, или события, не срабатывают ли кто-нибудь сталкивался хороший способ проверить @TransactionalEvents, находясь в состоянии @Rollback тестыТестирование @TransactionalEvents и @Rollback

+0

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

+0

Не уверен, что согласен, но я подумаю. @Rollback - это сброс базы данных, чтобы вернуться в исходное состояние теста (чтобы убедиться, что вероятность того, что тесты взаимодействуют друг с другом, меньше.) – adam

+0

Я знаю, что он делает. Проблема в том, что вы запрашиваете событие, которое совершила транзакция. Это не так. –

ответ

3

Stéphane Nicoll правильно: если TransactionPhase для.? ваш @TransactionalEventListener установлен на AFTER_COMMIT, тогда наличие транзакционного теста с автоматической семантикой отката не имеет никакого смысла, потому что событие никогда не получится уволена.

Другими словами, невозможно совершить какое-либо событие после совершения транзакции, если эта транзакция никогда не была совершена.

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

@Transactional 
@Commit 
@Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD, 
    config = @SqlConfig(transactionMode = TransactionMode.ISOLATED)) 
@Test 
public void test() { /* ... */ } 

С уважением,

Сэм (автор Spring TestContext Framework) решение

+0

Хорошо, спасибо. Поскольку я работал, несмотря на это, часть моего понимания недостатка заключается в том, что вложенные вызовы \ @Transactional независимо от распространения будут по-прежнему удерживать событие до тех пор, пока не будет выполнен самый высокий метод \ @Transactional. – adam

1

Сэма Brannen едва не работает учитывая комментарий адама.

На самом деле методы, аннотированные @TransactionalEventListener, вызывается после транзакции метода тестирования. Это происходит потому, что вызывающий метод, который вызывает событие, выполняется в рамках логической транзакции, а не физической.

Вместо этого, когда метод вызова выполняется в рамках новой физической транзакции, методы, аннотированные с помощью @TransactionalEventListener, вызываются в нужное время, то есть до того, как транзакция метода тестирования будет выполнена.

Кроме того, нам не нужны @Commit методы испытаний, так как мы фактически не заботимся об этих транзакциях. Однако нам нужен оператор @Sql(...), как объяснил Сэм Браннен, чтобы отменить совершенные изменения вызывающего метода.

См. Небольшой пример ниже.

Первый слушатель, который вызывается, когда сделка совершается (поведение @TransactionalEventListener по умолчанию):

@Component 
public class MyListener { 

    @TransactionalEventListener 
    public void when(MyEvent event) { 
     ... 
    } 
} 

Затем служба приложение, которое публикует событие прослушаны указанного класса.Обратите внимание на то, что транзакции выполнены так, чтобы быть новыми физическими, каждый раз, когда вызывается метод (см Spring Framework doc для более подробной информации):

@Service 
@Transactional(propagation = Propagation.REQUIRES_NEW) 
public class MyApplicationService { 

    public void doSomething() { 
     // ... 
     // publishes an instance of MyEvent 
     // ... 
    } 
} 

Наконец метод испытания, как предложил Сэм Brannen но без @Commit аннотацию, которая не является необходимо в данный момент:

@Transactional 
@Sql(scripts = "/cleanup.sql", executionPhase = AFTER_TEST_METHOD, 
    config = @SqlConfig(transactionMode = TransactionMode.ISOLATED)) 
@Test 
public void test() { 
    MyApplicationService target = // ... 
    target.doSomething(); 
    // the event is now received by MyListener 
    // assertions on the side effects of MyListener 
    // ... 
} 

Таким образом, он работает как шарм :-)