控制器返回ResponseEntity<StreamingResponseBody>测试数据不可见求助
解决方案:移除
@Transactional(NEVER)同时保证测试数据可见性与自动清理 针对你遇到的StreamingResponseBody异步控制器测试问题,核心矛盾在于:默认Spring测试事务的未提交数据无法被异步线程读取,而NEVER注解会导致数据残留、测试效率降低。以下是几种高效的解决思路:
1. 手动控制测试事务生命周期(推荐)
保留测试方法的@Transactional注解,通过TestTransactionAPI手动提交事务让异步线程可见,测试完成后再回滚清理数据,完全避免手动清理且效率更高。
示例代码:
import org.springframework.test.context.transaction.TestTransaction; import org.springframework.transaction.annotation.Transactional; import org.springframework.test.web.servlet.MvcResult; @Test @Transactional void testStreamingEndpoint() throws Exception { // 1. 生成并持久化测试数据 TestData testData = new TestData("test-content"); testEntityManager.persistAndFlush(testData); // 2. 提交当前事务,让异步线程能读取到数据 TestTransaction.flagForCommit(); TestTransaction.end(); // 3. 启动新事务,用于后续自动回滚 TestTransaction.start(); // 4. 执行MockMvc请求 MvcResult mvcResult = mockMvc.perform(get("/api/stream-data")) .andExpect(status().isOk()) .andReturn(); // 5. 获取异步结果并验证 StreamingResponseBody responseBody = (StreamingResponseBody) mvcResult.getAsyncResult(); // 执行你的验证逻辑... // 6. 标记当前事务回滚,测试结束后自动清理数据 TestTransaction.flagForRollback(); }
原理
- 测试初始事务提交后,异步线程(
StreamingResponseBody的执行线程)能读取到已提交的数据库数据 - 新启动的事务会在测试结束后自动回滚,完全避免数据残留
- 全程无需手动清理,效率远高于
NEVER注解的非事务测试
2. 数据准备与测试逻辑分离事务
将测试数据的准备逻辑放在独立的、使用REQUIRES_NEW传播级别的事务方法中,确保数据提前提交,测试方法仍使用默认的@Transactional(自动回滚)。
示例代码:
@BeforeEach void setupTestData() { prepareTestDataInIsolatedTransaction(); } @Transactional(propagation = Propagation.REQUIRES_NEW) void prepareTestDataInIsolatedTransaction() { TestData testData = new TestData("test-content"); entityManager.persist(testData); entityManager.flush(); } @Test @Transactional void testStreamingEndpoint() throws Exception { // 执行MockMvc请求与验证逻辑,无需关注数据清理 MvcResult mvcResult = mockMvc.perform(get("/api/stream-data")) .andExpect(status().isOk()) .andReturn(); StreamingResponseBody responseBody = (StreamingResponseBody) mvcResult.getAsyncResult(); // 验证逻辑... }
注意事项
- 此方法会在测试前向数据库提交真实数据,但测试方法的事务回滚不会影响已提交的准备数据,因此需要配合
@AfterEach或@Sql注解执行清理脚本,适合数据结构简单的场景。
3. 调整事务隔离级别(不推荐)
将测试事务的隔离级别设置为READ_UNCOMMITTED,允许异步线程读取测试事务中未提交的数据。但此方法会破坏事务的ACID特性,可能导致测试结果不可靠,仅作为临时替代方案。
示例代码:
@Test @Transactional(isolation = Isolation.READ_UNCOMMITTED) void testStreamingEndpoint() throws Exception { // 生成测试数据 TestData testData = new TestData("test-content"); testEntityManager.persistAndFlush(testData); // 执行MockMvc请求与验证 MvcResult mvcResult = mockMvc.perform(get("/api/stream-data")) .andExpect(status().isOk()) .andReturn(); StreamingResponseBody responseBody = (StreamingResponseBody) mvcResult.getAsyncResult(); // 验证逻辑... }
内容的提问来源于stack exchange,提问作者Kaj Hejer
相关产品推荐
相关产品推荐

