5

を使用して統合テストを作成していSpringJUnit4ClassRunnerます。私は基本クラスを持っています:

@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();
}

フローの最後に、エンティティが DB に保存されます。ただし、ここでの要件は、非同期で実行する必要があるということです。したがって、このメソッドが呼び出されると考えてください。

@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オブジェクトが DB に残されることです。最終的にすべての変更をロールバックするには、テスト トランザクションを作成する必要があります。

今、私がこれまでに試したこと:

  1. @Async注釈を削除します。明らかに、これは解決策にはなりませんが、それなしでロールバックが実行されることを確認するために行われました。確かにそうです。
  2. @AsyncアノテーションをHistoryWriter.saveHistory()メソッドに移動して、 で 1 か所にまとめ@Transactionalます。この記事https://dzone.com/articles/spring-async-and-transactionは、この方法で動作することを示唆していますが、私にとっては、テスト後にロールバックは行われません。
  3. これら 2 つの注釈の場所を入れ替えます。それは望ましい結果も与えません。

非同期メソッドで行われた DB 変更のロールバックを強制する方法を知っている人はいますか?

補足:

トランザクション構成:

<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"/>

4

1 に答える 1

6

非同期メソッドで行われた DB 変更のロールバックを強制する方法を知っている人はいますか?

残念ながらそれは不可能です。

Spring は、ThreadLocal変数を介してトランザクションの状態を管理します。したがって、別のスレッド (@Asyncメソッド呼び出し用に作成されたスレッドなど) で開始されたトランザクションは、親スレッドで管理されているトランザクションに参加できません。

これは、@Asyncメソッドで使用されるトランザクションが、Spring TestContext フレームワークによって自動的にロールバックされるテスト管理トランザクションと決して同じではないことを意味します。

したがって、問題を解決するには、データベースへの変更を手動で元に戻すしかありません。これをJdbcTestUtils行うには、SQL スクリプトをメソッド内でプログラム的に実行するか、Spring のアノテーション@AfterTransactionを介して(実行フェーズを使用して) 宣言的に実行されるように SQL スクリプトを構成できます。後者については、詳細については @Before メソッドの前に @Sql を実行する方法を参照してください。@Sql

よろしく、

Sam ( Spring TestContext フレームワークの作成者)

于 2016-08-20T14:37:43.807 に答える