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

Mockito单元测试使用ArgumentMatchers.eq()时参数变null及存根不匹配问题求解

问题根本原因

你遇到的两个核心报错都和Mockito本身的eq()逻辑无关,是代码设计和测试配置错误导致的:

  1. QuizMapper的存根完全不生效:你的SecurityService.verifyUser()方法中直接手动new QuizMapper()创建实例,测试类中不管用@Mock还是@Spy声明的QuizMapper实例,都不会被注入到业务代码中,两者完全是两个无关对象,你针对测试类QuizMapper的存根自然不会触发,所以调用getQuizFromQuizResponse()时参数和你定义的quizResponse毫无关联,看起来像是eq()没生效。
  2. FeignClient严格存根参数不匹配:
    • 业务类中@Value注入的accountId、workflowName、settingsMode字段,在单元测试默认配置下不会被赋值,全是默认值(数字为0,字符串为null)
    • 业务代码存在变量名笔误:verifyUser()方法入参是lexId,但你构建Verification时调用setId(Id)(变量名大小写错误),setMode(mode)(mode变量未定义,实际应该用成员变量settingsMode),导致最终构建的Verification对象和你测试类中存根用的verification属性不一致,eq()默认基于equals()方法比较,自然匹配失败触发严格存根报错。

修复步骤

1. 修复业务代码低级错误+依赖设计

首先调整SecurityService,将QuizMapper改为构造注入,同时修正变量名笔误:

@Slf4j
@Service
public class SecurityService {
    private final FeignClient feignClient;
    private final QuizMapper quizMapper; // 改为成员变量,不手动new
    @Value("${mode}")
    private String settingsMode;
    @Value("${accountId}")
    private long accountId;
    @Value("${workflowName}")
    private String workflowName;

    // 构造方法注入FeignClient和QuizMapper
    public SecurityService(FeignClient feignClient, QuizMapper quizMapper) {
        this.feignClient = feignClient;
        this.quizMapper = quizMapper;
    }

    public Quiz verifyUser(String lexId) throws ProviderException {
        // 修正变量名笔误
        Verification user = new VerificationBuilder()
                .setType(VerificationBuilder.TYPE)
                .setMode(settingsMode) // 用已注入的settingsMode
                .setId(lexId) // 用方法入参lexId
                .build();
        log.info("Verification POST request: {}", user);
        QuizResponse response = feignClient.checkIdentity(accountId, workflowName, user);
        log.info("QuizResponse POST response: {}", response);
        return quizMapper.getQuizFromQuizResponse(response);
    }
}

2. 修复单元测试配置

补充@Value字段的测试注入值,确保存根参数和实际调用参数一致:

@ExtendWith(MockitoExtension.class)
class SecurityServiceTest {
    @InjectMocks
    private SecurityService securityService;
    @Mock
    private FeignClient feignClient;
    @Mock
    private QuizMapper quizMapper;

    // 注入@Value字段的测试值
    @MockProperty("mode")
    private String mode = "test";
    @MockProperty("accountId")
    private long accountId = 1L;
    @MockProperty("workflowName")
    private String workflowName = "testWorkflow";

    @Test
    void testVerifyUserReturnsQuiz() throws ProviderException {
        String id = "Id";
        Quiz expectedQuiz = new Quiz();
        expectedQuiz.setId("quizId");
        // 和业务代码构建逻辑一致的Verification对象
        Verification verification = new VerificationBuilder()
                .setType(VerificationBuilder.TYPE)
                .setMode(mode)
                .setId(id)
                .build();
        QuizResponse quizResponse = new QuizResponse();
        quizResponse.setProducts(new ProductTestUtil().getProductList());

        given(feignClient.checkIdentity(eq(accountId), eq(workflowName), eq(verification)))
                .willReturn(quizResponse);
        given(quizMapper.getQuizFromQuizResponse(eq(quizResponse)))
                .willReturn(expectedQuiz);

        Quiz actualQuiz = securityService.verifyUser(id);
        assertEquals(expectedQuiz, actualQuiz);
    }
}

额外说明

如果你确实不想把QuizMapper注册为Bean,也可以把getQuizFromQuizResponse()改为静态方法,测试时用PowerMock等工具Mock静态方法即可,核心原则是保证业务代码调用的QuizMapper实例和你存根的实例是同一个。

内容的提问来源于stack exchange,提问作者frlzjosh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 00:39:04