Search code examples
spring-bootjunitmockitohamcrest

how to use mockito and junit to test my spring boot rest api?


I'm new in unit testing and cannot figure out how to test the RESTFul API with Spring using Mockito and Junit, first of all i have preapared the class in which i created two method with @Before and @Test, when i turned in debugging mode it tells me that hasSize collection return 0 not 4.

@RunWith(SpringJUnit4ClassRunner.class)
public class EmployeControllerTest {
    private MockMvc mockMvc;
    @InjectMocks
    private EmployeController employeController ; 
    @Mock
    private EmployeService employeService ;

    @Before
    public void setUp() throws Exception{
        MockitoAnnotations.initMocks(this);
        mockMvc=MockMvcBuilders.standaloneSetup(employeController).build();
    }
    @Test
    public void testgetAllEmployee() throws Exception{
        List<Employe> employes= Arrays.asList(
                new Employe("Hamza", "Khadhri", "hamza1007", "123")
                ,new Employe("Oussema", "smi", "oussama", "1234") );
        when(employeService.findAll()).thenReturn(employes);
        mockMvc.perform(get("/employe/dto"))
        .andExpect(status().isOk())
        .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
        .andExpect(jsonPath("$", hasSize(4)))
         .andExpect(jsonPath("$[0].nom", is("Hamza")))
         .andExpect(jsonPath("$[0].prenom", is("Khadhri")))
         .andExpect(jsonPath("$[0].login", is("hamza1007")))
         .andExpect(jsonPath("$[0].mp", is("123")))
         .andExpect(jsonPath("$[1].nom", is("Oussema")))
         .andExpect(jsonPath("$[1].prenom", is("smi")))
         .andExpect(jsonPath("$[1].login", is("oussama")))
         .andExpect(jsonPath("$[1].mp", is("1234")));

        verify(employeService,times(1)).findAll();
        verifyNoMoreInteractions(employeService);
    }
}

here is the EmployeController :

    @CrossOrigin(origins = "*", allowedHeaders = "*")
    @RestController
    @RequestMapping("/employe")
    public class EmployeController {
        @Autowired
        private EmployeService employeService;

        @Autowired
        private ModelMapper modelMapper;

        @GetMapping("/dto")
        public List<EmployeDTO> getEmployeDTOList(){
            try {
                    List<Employe> listemp=employeService.findAllEmployeActive();
                    return listemp.stream()
                        .map(emp ->convertToDto(emp))
                        .collect(Collectors.toList());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }

        private EmployeDTO convertToDto(Employe emp) {
            EmployeDTO empDto = modelMapper.map(emp, EmployeDTO.class);

            return empDto;
       }
   }

when I turn in debugging mode, it tells me that there is NullPointerException.

How to use properly mockito and junit to succeed the test?


Solution

  • I see a few potential issues:

    First, you are calling

    List<Employe> listemp=employeService.findAllEmployeActive();
    

    in your controller's getEmployeDTOList(), but your Mockito mock is written as:

    when(employeService.findAll()).thenReturn(employes)
    

    So, your test might not be working simply because your mock never happens.

    Second, you have not mocked the ModelMapper that you autowired in your controller. I think Spring will still go ahead and grab that component for you (someone correct me if I'm wrong there), but either way it's not great practice to have your unit tests be dependent on external libraries since you should only be concerned with the controller's functionality. It would be better to mock ModelMapper to make it "always" work and write separate tests to validate your mappings.

    I went ahead and made my own version of your code to test things out. I changed hasSize to 2 because you were only using two elements in your example.

    This test is working for me:

    @RunWith(SpringJUnit4ClassRunner.class)
    public class EmployeeControllerTest {
    
        private MockMvc mockMvc;
        @InjectMocks
        private EmployeeController employeeController ; 
        @Mock
        private EmployeeService employeeService;
        @Mock
        private ModelMapper modelMapper;
    
        @Before
        public void setUp() throws Exception{
            MockitoAnnotations.initMocks(this);
            mockMvc=MockMvcBuilders.standaloneSetup(employeeController).build();
        }
    
        @Test
        public void testgetAllEmployeeWithModelMapper() throws Exception{
            Employee emp1 = new Employee("Hamza", "Khadhri", "hamza1007", "123");
            Employee emp2 = new Employee("Oussema", "smi", "oussama", "1234");
            List<Employee> Employees= Arrays.asList(emp1, emp2);
    
            EmployeeDTO dto1 = new EmployeeDTO("Hamza", "Khadhri", "hamza1007", "123");
            EmployeeDTO dto2 = new EmployeeDTO("Oussema", "smi", "oussama", "1234");
            when(modelMapper.map(emp1,EmployeeDTO.class)).thenReturn(dto1);
            when(modelMapper.map(emp2,EmployeeDTO.class)).thenReturn(dto2);
    
            when(employeeService.findAll()).thenReturn(Employees);
    
    
            mockMvc.perform(get("/employe/dto"))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
                .andExpect(jsonPath("$", hasSize(2)))
                .andExpect(jsonPath("$[0].nom", is("Hamza")))
                .andExpect(jsonPath("$[0].prenom", is("Khadhri")))
                .andExpect(jsonPath("$[0].login", is("hamza1007")))
                .andExpect(jsonPath("$[0].mp", is("123")))
                .andExpect(jsonPath("$[1].nom", is("Oussema")))
                .andExpect(jsonPath("$[1].prenom", is("smi")))
                .andExpect(jsonPath("$[1].login", is("oussama")))
                .andExpect(jsonPath("$[1].mp", is("1234")));
    
            verify(employeeService,times(1)).findAll();
            verifyNoMoreInteractions(employeeService);
    
        }
    
    }
    

    As you can see I create my own DTO objects and pass them back so that modelMapper will always behave as expected for this unit test.