Mockito单元测试使用ArgumentMatchers.eq()时参数变null及存根不匹配问题求解
问题根本原因
你遇到的两个核心报错都和Mockito本身的eq()逻辑无关,是代码设计和测试配置错误导致的:
- QuizMapper的存根完全不生效:你的
SecurityService.verifyUser()方法中直接手动new QuizMapper()创建实例,测试类中不管用@Mock还是@Spy声明的QuizMapper实例,都不会被注入到业务代码中,两者完全是两个无关对象,你针对测试类QuizMapper的存根自然不会触发,所以调用getQuizFromQuizResponse()时参数和你定义的quizResponse毫无关联,看起来像是eq()没生效。 - 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
相关产品推荐
相关产品推荐

