简体   繁体   中英

jmockit expectation keeps throwing missing invocation even though I'm calling it

I am creating a Spring application and below are the related controller, repository,test, and the error msg.

My application has no problem in terms of functionality whatsoever but it's not passing this one unit test.

I mainly have a problem with the Expectations method inside the jmockit test unit. I can't see WHY it is keep saying that I am missing an invocation?? Eventhough I do call them at the very last else block of my controller

skillRepository.findAllById((List<Integer>))
job.setSkills((List<Skill>))

I have been combing through others' similar questions here but could not solve my problem. Thank you in advance.

Controller

@PostMapping("add")
    public String processAddJobForm(@ModelAttribute @Valid Job newJob,
                                       Errors errors, Model model, @RequestParam int employerId,
                                    @RequestParam List<Integer> skills) {


        if(errors.hasErrors() || skills.size()==1){
            if(skills.size()==1){
                model.addAttribute("skills_error","You have to choose at least one skill");
            }
            model.addAttribute("title","Add Job");
            model.addAttribute("employers",employerRepository.findAll());
            model.addAttribute("skills",skillRepository.findAll());
            return "add";
        }
        Optional<Employer> result = employerRepository.findById(employerId);

        if(result.isEmpty()){
            model.addAttribute("title","Invalid Employer ID: "+employerId);
            return "add";
        }else{
            Employer employer = result.get();
            newJob.setEmployer(employer);

            List<Skill> skillObjs = (List<Skill>) skillRepository.findAllById(skills);
            newJob.setSkills(skillObjs);

            jobRepository.save(newJob);

            return "redirect:";
        }

    }

Repository

@Repository
public interface SkillRepository extends CrudRepository<Skill,Integer> {
}

Test unit

/*
    * Verifies that HomeController.processAddJobForm queries skillRepository and sets skills properly
    * */
    @Test
    public void testProcessAddJobFormHandlesSkillsProperly (
            @Mocked SkillRepository skillRepository,
            @Mocked EmployerRepository employerRepository,
            @Mocked JobRepository jobRepository,
            @Mocked Job job,
            @Mocked Errors errors)
            throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException {
        Class homeControllerClass = getClassByName("controllers.HomeController");
        Method processAddJobFormMethod = homeControllerClass.getMethod("processAddJobForm", Job.class, Errors.class, Model.class, int.class, List.class);

        new Expectations() {{
            skillRepository.findAllById((List<Integer>) any);
            job.setSkills((List<Skill>) any);
        }};

        Model model = new ExtendedModelMap();
        HomeController homeController = new HomeController();

        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);

        processAddJobFormMethod.invoke(homeController,  job, errors, model, 0, new ArrayList<Skill>());
    }

The Error

Missing 1 invocation to:
org.springframework.data.repository.CrudRepository#findAllById(any Iterable)
   on mock instance: org.launchcode.techjobs.persistent.models.data.$Impl_SkillRepository@7030449d
Missing 1 invocation to:
org.springframework.data.repository.CrudRepository#findAllById(any Iterable)
   on mock instance: org.launchcode.techjobs.persistent.models.data.$Impl_SkillRepository@7030449d
    at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: Missing invocations
    at org.launchcode.techjobs.persistent.TestTaskFour$1.<init>(TestTaskFour.java:143)
    at org.launchcode.techjobs.persistent.TestTaskFour.testProcessAddJobFormHandlesSkillsProperly(TestTaskFour.java:142)
    ... 1 more


org.launchcode.techjobs.persistent.TestTaskFour > testProcessAddJobFormHandlesSkillsProperly(SkillRepository, EmployerRepository, JobRepository, Job, Errors) FAILED
    mockit.internal.expectations.invocation.MissingInvocation
        Caused by: mockit.internal.expectations.invocation.ExpectationError at TestTaskFour.java:143
1 test completed, 1 failed
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/JK/Desktop/LaunchCode/LC_Java/assignment-4-techjobs-persistent-edition-suchunkang0822/build/reports/tests/test/index.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 2s
4 actionable tasks: 1 executed, 3 up-to-date

It's difficult to tell as you only show the test, and not the rest of the test class. However, I'm guessing that on the tested class, "employerRepository" and "skillsRepoisitory" are likely auto-wired. Assuming so, then you likely want to make use of "@Tested" and "@Injectable" as opposed to "@Mocked". That is, your class is "@Tested" and the thing it needs at construction is passed as @Injectable.

public class DummyTest {
  @Tested public HomeController ctrl;
  @Injectable protected SkillRepository skillRepository;
  @Injectable protected EmployerRepository employerRepository;
  @Injectable protected JobRepository jobRepository;
  @Test public void testSomething(..) { .. }
}

Your second issue is that you aren't actually testing with the mocks you create. In your test, you created a "real" HomeController via new, and then, via reflection, invoked processAddJobForm(..) on it. It's much simpler:

@Test
public void testProcessAddJobFormHandlesSkillsProperly (
        @Mocked Job job,
        @Mocked Errors errors)
    {

    new Expectations() {{
        skillRepository.findAllById((List<Integer>) any);
        job.setSkills((List<Skill>) any);
    }};

    Model model = new ExtendedModelMap();
    homeController.processAddJobForm(job, errors, model, 0, new ArrayList<Skill>());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM