测试LocationService异常场景遇空指针,求无源码修改的解决方案
测试getLocationById异常场景时抛出NullPointerException而非预期CustomException的问题
测试LocationServiceImpl的getLocationById方法异常场景时,当LocationRepository查询不到对应locationId时,预期抛出CustomException,但实际抛出了NullPointerException。已尝试通过when(locationRepository.findById(anyString())).thenReturn(Optional.empty())模拟数据,也将anyString替换为随机字符串,但问题未解决。调试发现异常出现在Location location = locationRepository.findById(locationId).orElseThrow代码块中,希望在不修改业务源码的前提下完成测试。
业务服务方法
@Slf4j @Service @AllArgsConstructor public class LocationServiceImpl implements LocationService { private final LocationRepository locationRepository; public LocationMobieDTO getLocationById(HttpHeaders headers, String locationId) { credentialService.checkRegistered(headers); Location location = locationRepository.findById(locationId).orElseThrow(() -> { KafkaMessageDTO<String> messageDTO = KafkaMessageDTO.<String>builder().id(locationId).build(); messageServiceString.sendMessage( KafkaTopics.TEST.topic, messageServiceString.prepareMsg(ResponseEnum.UNKNOWN_LOCATION, messageDTO) ); return new CustomException(ResponseEnum.UNKNOWN_LOCATION); }); return mapper.mapperLocationToDTO(location); } }
测试用例
@ExtendWith(MockitoExtension.class) public class LocationServiceTest { @InjectMocks private LocationServiceImpl locationService; @Mock private LocationRepository locationRepository; @Test void whenGetLocationById_thenReturnCustomException() { // Given when(locationRepository.findById(anyString())).thenReturn(Optional.empty()); // When CustomException exception = assertThrows(CustomException.class, () -> { locationService.getLocationById(headers, anyString()); }); // Then assertEquals(ResponseEnum.UNKNOWN_LOCATION, exception.getException()); } }
控制器代码
@GetMapping(value = "/{country_code}/{party_id}/{location_id}") public Response<LocationMobieDTO> getLocationById(@RequestHeader HttpHeaders headers, @PathVariable("location_id") String locationId){ return Response.<LocationMobieDTO>builder() .data(locationService.getLocationById(headers, locationId)) .statusCode(ResponseEnum.SUCCESS.getStatusCode()) .statusMessage(ResponseEnum.SUCCESS.getStatusMessage()) .timestamp(Instant.now().toString()).build(); }
错误信息
org.opentest4j.AssertionFailedError: Unexpected exception type thrown ==] expected: [com.demo.myapi.exception.CustomException] but was: [java.lang.NullPointerException] at com.demo.myapi.location.service.LocationServiceTest.whenGetLocationById_thenReturnCustomException(LocationServiceTest.java:177) at java.base/java.util.ArrayList.forEach(ArrayList.java:1541) at java.base/java.util.ArrayList.forEach(ArrayList.java:1541) Caused by: java.lang.NullPointerException at com.demo.myapi.location.service.LocationServiceImpl.lambda$0(LocationServiceImpl.java:95) at java.base/java.util.Optional.orElseThrow(Optional.java:408) at com.demo.myapi.location.service.LocationServiceImpl.getLocationById(LocationServiceImpl.java:91) at com.demo.myapi.location.service.LocationServiceTest.lambda$0(LocationServiceTest.java:178) ... 72 more
解决方案
问题分析
从错误栈可以看出,NullPointerException发生在orElseThrow的lambda表达式中,根源是业务方法中依赖的credentialService、messageServiceString、mapper以及测试用例中的headers都没有被Mock,处于null状态,调用它们的方法时直接抛出NPE,导致还没来得及返回CustomException就报错。
修复步骤
- 在测试类中添加所有业务方法依赖的Mock对象:
@ExtendWith(MockitoExtension.class) public class LocationServiceTest { @InjectMocks private LocationServiceImpl locationService; @Mock private LocationRepository locationRepository; // 添加以下Mock对象 @Mock private CredentialService credentialService; @Mock private MessageServiceString messageServiceString; @Mock private YourMapperClass mapper; // 替换为实际的mapper类名 @Mock private HttpHeaders headers;
- 在测试方法中,对需要执行的无返回值方法做空操作Mock,避免调用时抛出异常:
@Test void whenGetLocationById_thenReturnCustomException() { // Given when(locationRepository.findById(anyString())).thenReturn(Optional.empty()); // 模拟credentialService的checkRegistered方法无操作 doNothing().when(credentialService).checkRegistered(any(HttpHeaders.class)); // When CustomException exception = assertThrows(CustomException.class, () -> { // 替换anyString()为具体字符串,避免匹配器使用不一致问题 locationService.getLocationById(headers, "test-location-id"); }); // Then assertEquals(ResponseEnum.UNKNOWN_LOCATION, exception.getException()); }
这样修改后,所有依赖都被正确Mock,不会触发NPE,业务代码会正常执行到抛出CustomException的逻辑,测试即可通过。
内容的提问来源于stack exchange,提问作者User
相关产品推荐
相关产品推荐

