SpringBoot JUnit测试@Async方法时Mockito空对象与数据查询问题
Spring Boot JUnit环境@Async异步方法正确测试方案
核心问题说明
三个问题的根因分别为:
- PROXY模式下查询不到刚保存的数据:测试主线程未等待异步方法执行完成就触发了查询,此时JPA的saveAll操作还未执行
- Mockito测试时mock对象为null:@Async生成的代理类未获取到注入的mock对象,通常是因为使用了
@Mock而非Spring上下文识别的@MockBean,或是直接手动实例化了被@Async修饰的类 - ASPECTJ模式下异步不生效:测试环境未开启AspectJ织入(编译时/加载时织入),导致@Async注解未被识别
分场景解决方案
场景1:真实Spring上下文测试(实际操作数据库,使用默认PROXY模式)
不需要修改@EnableAsync的模式,用以下两种方案解决异步等待问题即可:
- 方案1:修改异步方法返回值为Future,测试时阻塞等待执行完成
不需要引入额外依赖,仅需对业务方法做小调整:
测试代码调整为:@Async public Future<Void> asyncMethod (){ // 原有业务代码 repositoryCollection.clusterRepository.saveAll(); return CompletableFuture.completedFuture(null); }@Test public void testAsyncMethod () throws ExecutionException, InterruptedException { // 原有测试前置代码 asyncMethod().get(); // 阻塞主线程直到异步方法执行完成 List<Cluster> result = repositoryCollection.clusterRepository.listByClusterID(); // 后续断言逻辑 } - 方案2:使用Awaitility工具等待数据符合预期,无需修改业务代码
引入Awaitility测试依赖后,直接在测试中异步等待查询结果符合预期即可:@Test public void testAsyncMethod (){ // 原有测试前置代码 asyncMethod(); // 最多等待3秒,每100毫秒查询一次,直到结果符合预期 Awaitility.await() .atMost(Duration.ofSeconds(3)) .pollInterval(Duration.ofMillis(100)) .until(() -> !repositoryCollection.clusterRepository.listByClusterID().isEmpty()); // 后续断言逻辑 }
场景2:Mockito单元测试@Async方法,解决mock为null问题
必须使用Spring测试上下文托管被@Async修饰的类,且用@MockBean注入依赖的mock对象:
@SpringBootTest @EnableAsync class AsyncServiceTest { // 注入Spring生成的带异步代理的服务实例 @Autowired private AsyncService asyncService; // 用@MockBean将mock对象注入Spring上下文,代理类会自动获取该实例 @MockBean private RepositoryCollection repositoryCollection; @Test public void testAsyncMethod () throws ExecutionException, InterruptedException { // 提前mock依赖方法的返回值 ClusterRepository mockRepo = Mockito.mock(ClusterRepository.class); when(repositoryCollection.getClusterRepository()).thenReturn(mockRepo); asyncService.asyncMethod().get(); // 验证方法调用符合预期 verify(mockRepo, times(1)).saveAll(anyList()); } }
场景3:ASPECTJ模式异步不生效修复
如果必须使用ASPECTJ模式,需要在测试环境开启织入配置:
- 引入
spring-boot-starter-aop、aspectjweaver、spring-instrument依赖 - 测试启动参数添加JVM参数:
-javaagent:path/to/aspectjweaver.jar -javaagent:path/to/spring-instrument.jar - 测试配置类添加
@EnableLoadTimeWeaving注解
普通测试场景优先使用PROXY模式,无需额外配置成本。
内容的提问来源于stack exchange,提问作者Pyke
相关产品推荐
相关产品推荐

