Spring单元测试JMockit报missing invocation缺失调用错误
问题根因
测试报错的核心原因是测试执行流程根本没有走到Controller里调用skillRepository.findAllById和job.setSkills的else分支,两个配置的预期调用自然不会触发,具体触发点有两个:
- 你没有为Mock的
employerRepository.findById()方法配置返回值,JMockit默认对返回Optional类型的Mock方法返回空Optional,执行到if(result.isEmpty())判断时会直接进入该分支返回,不会走到后续else逻辑。 - 反射调用Controller方法时传入的skills参数类型错误:方法要求入参是
List<Integer>,你实际传入的是new ArrayList<Skill>(),且传入的employerId为0,和Mock逻辑不匹配,进一步导致流程偏离预期分支。
修复方案
按以下步骤调整测试代码即可:
- 为
employerRepository.findById配置Mock返回值,返回包含Employer实例的非空Optional,保证流程进入else块 - 修正传入Controller的skills参数,改为元素为Integer、长度大于1的列表,避免触发前置if判断的提前返回
- 为
skillRepository.findAllById配置Mock返回值,避免返回null导致逻辑异常 - 保证传入的employerId和Mock配置的入参匹配
修复后测试代码示例
@Test public void testProcessAddJobFormHandlesSkillsProperly ( @Mocked SkillRepository skillRepository, @Mocked EmployerRepository employerRepository, @Mocked JobRepository jobRepository, @Mocked Job job, @Mocked Errors errors, @Mocked Employer testEmployer) throws Exception { Class homeControllerClass = getClassByName("controllers.HomeController"); Method processAddJobFormMethod = homeControllerClass.getMethod("processAddJobForm", Job.class, Errors.class, Model.class, int.class, List.class); // 测试数据准备 int testEmployerId = 1; List<Integer> testSkillIds = Arrays.asList(1,2); List<Skill> testSkillObjs = new ArrayList<>(); new Expectations() {{ // 配置employerRepository返回非空Optional,保证进入else分支 employerRepository.findById(testEmployerId); result = Optional.of(testEmployer); // 配置skillRepository返回值 skillRepository.findAllById((List<Integer>) any); result = testSkillObjs; // 预期调用 job.setSkills((List<Skill>) any); }}; Model model = new ExtendedModelMap(); HomeController homeController = new HomeController(); // 注入Mock的Repository Field skillRepositoryField = homeControllerClass.getDeclaredField("skillRepository"); skillRepositoryField.setAccessible(true); skillRepositoryField.set(homeController, skillRepository); Field employerRepositoryField = homeControllerClass.getDeclaredField("employerRepository"); employerRepositoryField.setAccessible(true); employerRepositoryField.set(homeController, employerRepository); Field jobRepositoryField = homeControllerClass.getDeclaredField("jobRepository"); jobRepositoryField.setAccessible(true); jobRepositoryField.set(homeController, jobRepository); // 修正入参:传入正确类型的skills列表、匹配的employerId,errors默认hasErrors返回false不会触发前置分支 processAddJobFormMethod.invoke(homeController, job, errors, model, testEmployerId, testSkillIds); }
补充说明:你Controller里的前置判断逻辑有小缺陷,当前只判断了
skills.size()==1的场景,当skills长度为0(用户没选任何技能)时会直接走到持久化逻辑,建议调整为skills.size() < 1覆盖空选择场景,不过这个不影响当前测试通过。
内容的提问来源于stack exchange,提问作者Kang_the_Conqueror
相关产品推荐
相关产品推荐

