Spring Boot中@Async方法的单元测试(MockitoJUnitRunner实现)
用MockitoJUnitRunner测试返回AsyncResult的@Async方法
没问题,我来一步步教你怎么给这个带@Async注解的方法写单元测试,重点是正确处理返回的AsyncResult<Map<ObjectId, Field>>类型。
第一步:配置测试类基础结构
首先,你的测试类要使用MockitoJUnitRunner,并注入被测试的服务类(就是包含这个execute方法的类)。如果这个方法依赖其他Bean,记得用@Mock模拟这些依赖,再通过@InjectMocks注入到被测试类里。
第二步:编写测试方法示例
下面是完整的测试代码示例,我会加上注释说明关键步骤:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.scheduling.annotation.AsyncResult; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class YourServiceTest { // 替换成你的测试类名 @InjectMocks @Spy // 如果需要部分mock被测试类的方法(比如executeUniqueValidationPerCollection),用@Spy private YourService yourService; // 替换成你的实际服务类名 @Test public void testExecute_WhenNoValidations_ReturnsInputMap() throws Exception { // 1. 准备测试数据 Map<ObjectId, Field> inputFieldValuesMap = new HashMap<>(); ObjectId testFieldId = new ObjectId("507f1f77bcf86cd799439011"); // 示例ObjectId Field testField = new Field(); // 根据你的Field类构造实际测试对象 inputFieldValuesMap.put(testFieldId, testField); List<GlobalValidation> emptyValidations = Collections.emptyList(); Map<ObjectId, ValidationField> emptyEvalMap = new HashMap<>(); // 2. (可选)如果不想执行executeUniqueValidationPerCollection的实际逻辑,mock它 doNothing().when(yourService).executeUniqueValidationPerCollection(any(GlobalValidation.class), anyMap(), anyMap()); // 3. 调用异步方法 Future<Map<ObjectId, Field>> resultFuture = yourService.execute(emptyValidations, emptyEvalMap, inputFieldValuesMap); // 4. 获取AsyncResult里的实际结果,注意处理异常 Map<ObjectId, Field> resultMap = resultFuture.get(); // 5. 验证结果是否符合预期 assertEquals(inputFieldValuesMap.size(), resultMap.size()); assertEquals(testField, resultMap.get(testFieldId)); } @Test public void testExecute_WithValidations_ReturnsUpdatedMap() throws Exception { // 这里可以写有验证规则的测试场景,比如构造带数据的globalUniqueValidations // 同样的步骤:准备数据 -> 调用方法 -> 验证结果 Map<ObjectId, Field> inputMap = new HashMap<>(); // 填充测试数据... List<GlobalValidation> validations = Collections.singletonList(new GlobalValidation()); // 初始化验证规则... Future<Map<ObjectId, Field>> resultFuture = yourService.execute(validations, new HashMap<>(), inputMap); Map<ObjectId, Field> result = resultFuture.get(); // 根据业务逻辑验证结果,比如某个Field的状态是否被修改 // assertEquals(expectedField, result.get(testId)); } // 如果需要mock整个execute方法的返回值(而不是实际调用) @Test public void testExecute_MockedReturnValue() throws Exception { Map<ObjectId, Field> mockResultMap = new HashMap<>(); // 构造mock的返回Map... // 模拟execute方法返回指定的AsyncResult when(yourService.execute(anyList(), anyMap(), anyMap())) .thenReturn(new AsyncResult<>(mockResultMap)); // 调用方法 Future<Map<ObjectId, Field>> resultFuture = yourService.execute(null, null, null); Map<ObjectId, Field> result = resultFuture.get(); // 验证返回的是我们mock的Map assertEquals(mockResultMap, result); } }
关键注意点
- 处理Future结果:因为
@Async方法返回Future,所以必须调用get()方法来获取AsyncResult封装的实际Map,记得捕获InterruptedException和ExecutionException(或者直接抛出,像示例里那样)。 - 部分Mock被测试类:如果
executeUniqueValidationPerCollection是被测试类的私有方法,你可能需要把它改成protected或者包访问权限,才能用@Spy和doNothing()来跳过实际逻辑;如果它是另一个Bean的方法,直接用@Mock模拟那个Bean即可。 - AsyncResult的本质:
AsyncResult是Spring提供的Future实现,所以测试时不需要额外处理异步线程——因为在单元测试环境下,@Async默认会同步执行(除非你特意配置了异步线程池),所以调用方法后立刻就能拿到结果。
内容的提问来源于stack exchange,提问作者user_531
相关产品推荐
相关产品推荐

