2016-08-19 9 views
2

Я пишу интеграционный тест, используя SpringJUnit4ClassRunner. У меня есть базовый класс:Откат транзакции JUnit с методом @Async

@RunWith(SpringJUnit4ClassRunner.class) 
@WebAppConfiguration 
@ContextConfiguration({ /*my XML files here*/}) 
@Ignore 
public class BaseIntegrationWebappTestRunner { 

@Autowired 
protected WebApplicationContext wac; 

@Autowired 
protected MockServletContext servletContext; 

@Autowired 
protected MockHttpSession session; 

@Autowired 
protected MockHttpServletRequest request; 

@Autowired 
protected MockHttpServletResponse response; 

@Autowired 
protected ServletWebRequest webRequest; 

@Autowired 
private ResponseTypeFilter responseTypeFilter; 

protected MockMvc mockMvc; 

@BeforeClass 
public static void setUpBeforeClass() { 

} 

@AfterClass 
public static void tearDownAfterClass() { 

} 

@Before 
public void setUp() { 
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(responseTypeFilter).build(); 
} 

@After 
public void tearDown() { 
    this.mockMvc = null; 
} 
} 

Тогда я продлить его и создать тест с использованием mockMvc:

public class MyTestIT extends BaseMCTIntegrationWebappTestRunner { 

@Test 
@Transactional("jpaTransactionManager") 
public void test() throws Exception { 
    MvcResult result = mockMvc 
      .perform(
        post("/myUrl") 
          .contentType(MediaType.APPLICATION_XML) 
          .characterEncoding("UTF-8") 
          .content("content") 
          .headers(getHeaders()) 
      ).andExpect(status().isOk()) 
      .andExpect(content().contentType(MediaType.APPLICATION_XML)) 
      .andExpect(content().encoding("ISO-8859-1")) 
      .andExpect(xpath("/*[local-name() ='myXPath']/") 
        .string("result")) 
      .andReturn(); 
} 

В конце потока, объект сохраняется в БД. Но требование здесь состоит в том, что это должно выполняться асинхронно. Так считают этот метод называется:

@Component 
public class AsyncWriter { 

    @Autowired 
    private HistoryWriter historyWriter; 

    @Async 
    public void saveHistoryAsync(final Context context) { 
     History history = historyWriter.saveHistory(context); 
    } 
} 

HistoryWriter Тогда называется:

@Component 
public class HistoryWriter { 

    @Autowired 
    private HistoryRepository historyRepository; 

    @Transactional("jpaTransactionManager") 
    public History saveHistory(final Context context) { 
     History history = null; 
     if (context != null) { 
      try { 
       history = historyRepository.saveAndFlush(getHistoryFromContext(context)); 
      } catch (Throwable e) { 
       LOGGER.error(String.format("Cannot save history for context: [%s] ", context), e); 
      } 
     } 
     return history; 
    } 
} 

Проблема со всем этим в том, что после того, как тест будет сделано, History объект остается в БД. Мне нужно сделать тестовую транзакцию для отмены всех изменений в конце.

Теперь, что я пытался до сих пор:

  1. Удалить @Async аннотацию. Очевидно, это не может быть решением, но было сделано, чтобы подтвердить, что откат будет выполняться без него. В самом деле.
  2. Переместить @Async аннотация к HistoryWriter.saveHistory() метод иметь его в одном месте с @Transactional. В этой статье https://dzone.com/articles/spring-async-and-transaction предлагается, чтобы она работала таким образом, но для меня откат не выполняется после теста.
  3. Обмен местами этих двух аннотаций. Это также не дает желаемого результата.

Кто-нибудь есть идеи, как принудительно отменить изменения базы данных в асинхронном режиме?

Боковые ноты:

конфигурация сделки:

<tx:annotation-driven proxy-target-class="true" transaction- manager="jpaTransactionManager"/>

Асинхронный конфигурации:

<task:executor id="executorWithPoolSizeRange" pool-size="50-75" queue-capacity="1000" /> <task:annotation-driven executor="executorWithPoolSizeRange" scheduler="taskScheduler"/>

ответ

3

Кто-нибудь есть идеи, как заставить откат БД изменения сделано в асинхронном методе?

Это, к сожалению, невозможно.

Spring управляет состоянием сделки через ThreadLocal переменных. Транзакция началась в другом потоке (например, созданном для вашего вызова @Async), поэтому не участвует в транзакции, управляемой для родительского потока.

Это означает, что транзакция используется вашим методом @Async является никогда такой же, как test-managed transaction, который получает автоматически откатился в Spring Framework TestContext.

Таким образом, единственное возможное решение проблемы: вручную отменить изменения в базе данных. Вы можете сделать это с помощью JdbcTestUtils для выполнения SQL-скрипта программным способом в рамках метода @AfterTransaction или вы можете альтернативно конфигурировать SQL-скрипт, который должен быть выполнен декларативно посредством аннотации Spring @Sql (с использованием после этапа выполнения). Для последних см. How to execute @Sql before a @Before method.

С уважением,

Сэм (автор Spring Framework TestContext)

+0

Большое спасибо за ваш ответ. Поскольку я не мог архивировать это, и теперь я знаю, что это невозможно, я закончил создание отдельного Spring config xml для тестирования, в котором удалялась '<задача: аннотация, управляемая исполнителем =" executorWithPoolSizeRange "scheduler =" taskScheduler "/>' so @Async не будет работать во время тестирования, и, таким образом, транзакция будет отменена. – dty

+0

Теперь у меня есть еще одна задача, не могли бы вы взглянуть? Мне нужно было добавить PowerMock в свои тестовые примеры в дополнение к SpringJUnit4ClassRunner. Для этого я добавил '@RunWith (PowerMockRunner.class) @PowerMockRunnerDelegate (SpringJUnit4ClassRunner.class)' И теперь я также хотел бы использовать Parameterized runner. Я проверил https://jira.spring.io/browse/SPR-5292, и, похоже, я могу использовать Spring Rules, чтобы заставить его работать с параметризованным. Но будет ли он работать и с PowerMock? Я попробовал делегировать PowerMock для параметра Parameterized и добавлять правила Spring, но не работал. Это можно сделать? – dty

+0

Я никогда не пробовал эту комбинацию, поэтому я не знаю, работает она или нет. –

 Смежные вопросы

  • Нет связанных вопросов^_^