You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:45:53