WebTestClient调用接口无法获取JpaRepository已存数据的方案咨询
问题:Spring Boot集成测试中WebTestClient无法读取测试线程插入的数据
我编写了一个Spring Boot集成测试用例:先通过dataRepository.saveAndFlush()插入数据,测试方法内直接调用dataRepository.findAll()能查到该数据,但使用WebTestClient调用/api/constant接口时,接口中同样调用dataRepository.findAll()却返回null。
Controller代码
@RequestMapping(value = "/api/constant") public class ConfigurationController { @Autowired private DataRepository dataRepository; @GetMapping public Mono<Object> getConstant() { Mono.fromSupplier(() -> this.dataRepository.findAll()).subscribeOn(Schedulers.single()); } }
测试类代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureWebTestClient @Transactional public class TestITCase { @Autowired private DataRepository dataRepository; @Autowired private WebTestClient webTestClient; @Test public void getConstants_Valid_Success() { this.dataRepository.saveAndFlush(data); this.dataRepository.findAll(); // 数据存在 FluxExchangeResult<String> result = this.webTestClient.get() .uri(b -> b.path("/api/constant") .build()) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .exchange() .returnResult(String.class) .getResponseBody() .blockFirst(); // 此处返回null } }
更新:已知WebTestClient的请求会在不同线程处理,因此使用了新事务。现想咨询是否可以让WebTestClient运行在同一线程,因为不想提交事务再回滚,避免测试失败导致数据残留,破坏测试幂等性。
解决方案
核心原因分析
- 测试类的
@Transactional事务绑定在测试线程上,WebTestClient默认用异步线程处理请求,异步线程无法访问测试线程中未提交的事务数据。 - Controller代码存在错误:方法创建了Mono但未返回,且
subscribeOn(Schedulers.single())强制切换线程,导致事务上下文丢失。
步骤1:修复Controller代码
首先要确保Controller正确返回响应,且避免不必要的线程切换:
@RequestMapping(value = "/api/constant") public class ConfigurationController { @Autowired private DataRepository dataRepository; @GetMapping public Mono<List<Object>> getConstant() { // 直接返回由Repository查询生成的Mono,不强制切换线程 return Mono.fromSupplier(dataRepository::findAll); } }
步骤2:让WebTestClient在测试线程同步执行
通过配置WebTestClient使用同步调度器,让请求处理与测试线程在同一线程,从而共享事务上下文:
自定义WebTestClient配置(测试类中)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @Transactional public class TestITCase { @Autowired private DataRepository dataRepository; @Autowired private ApplicationContext context; private WebTestClient webTestClient; @BeforeEach void setUp() { // 绑定到应用上下文,指定使用即时调度器(同步执行) this.webTestClient = WebTestClient.bindToApplicationContext(context) .scheduler(Schedulers.immediate()) .configureClient() .build(); } @Test public void getConstants_Valid_Success() { this.dataRepository.saveAndFlush(data); // 验证测试线程能查到数据 assert !this.dataRepository.findAll().isEmpty(); String result = this.webTestClient.get() .uri("/api/constant") .accept(MediaType.APPLICATION_JSON) .exchange() .expectStatus().isOk() .expectBody(String.class) .returnResult() .getResponseBody(); assert result != null; } }
关键注意事项
- 避免在Controller中随意使用
subscribeOn切换线程,除非业务明确需要异步处理,否则保持请求线程与业务逻辑线程一致。 - 使用
Schedulers.immediate()确保WebTestClient的请求处理在测试线程执行,这样就能读取到@Transactional注解下未提交的测试数据。 - 保留
@Transactional的默认行为(测试结束自动回滚),无需手动提交事务,确保测试的幂等性,避免数据残留。
内容的提问来源于stack exchange,提问作者Steven Kristian
相关产品推荐
相关产品推荐

