I’m trying to write unit test for my service layer:
@SpringBootTest class ClinicServiceTest { @Mock private ProcedureRepository procedureRepository; @InjectMocks private ClinicService clinicService; @Test void setProcedureStatus() { when(procedureRepository.findById(1L)).thenReturn(Optional.of(initialEntity)); when(procedureRepository.saveAndFlush(expectedEntity)).thenReturn(expectedEntity); Procedure assertProcedure = clinicService.setProcedureStatus(1L, "CANCELED"); } }
When i call setProcedureStatus
method it throws NullPointerException because my service class uses ModelMapper that is being autowired by Spring:
@Service @RequiredArgsConstructor public class ClinicService { private final ProcedureRepository procedureRepository; private final ModelMapper modelMapper; public Procedure setProcedureStatus(Long procedureId, String status) { ... return modelMapper.map(procedureEntity, Procedure.class); }
}
Unit test doesn’t raise Spring context, that’s the reason why ModelMapper is null in Service when i call it from test. Is there ways to solve this problem?
Answer
You want to write a unit test so you shouldn’t raise the spring context. Unit tests are faster than Integration tests (where often you want to raise your spring context). When it comes to unit tests you want to test unit part of your application.
It this case you are getting NPE because you didn’t initialize ModelMapper.
I didn’t see where you are creating your ClinicService, but there is one example how you can avoid that NPE:
@Test void setProcedureStatus() { when(procedureRepository.findById(1L)).thenReturn(Optional.of(initialEntity)); when(procedureRepository.saveAndFlush(expectedEntity)).thenReturn(expectedEntity); // Initialize ClinicService with mocked procedeRepository but real ModelMapper ClinicService clinicService = new ClinicService(procedureRepository, new ModelMapper(), ... other dependencies ); Procedure assertProcedure = clinicService.setProcedureStatus(1L, "CANCELED"); }
or you can mock ModelMapper too.
Another way is to use MockitoJunitRunner and initialize your class under tests like below:
@RunWith(MockitoJUnitRunner.class) public class ExampleTest { @Mock private ModelMapper modelMapper; @Mock private Repository repository; @InjectMocks private ClinicService classUnderTest;
Thanks to dependency injection your code is much simplier to test because you can just create your service with dependencies passed via constructor.