使用Mockito测试Micronaut HttpClient(Java)时遇空指针异常
问题:Mock HttpClient.toBlocking().exchange()时触发空指针异常
尝试测试服务类方法行为,需要Mock HttpClient.toBlocking().exchange()的返回值,但使用when-thenReturn设置时抛出空指针异常:
java.lang.NullPointerException: Cannot invoke "io.micronaut.http.client.BlockingHttpClient.exchange(io.micronaut.http.HttpRequest, java.lang.Class)" because the return value of "io.micronaut.http.client.HttpClient.toBlocking()" is null
测试代码如下:
@MicronautTest public class WeatherServiceTest { static final String HOUSE_NUM = "testNum"; static final String STREET = "testStreet"; static final String POST_CODE = "testPostCode"; static final String CITY = "testCity"; @Mock HttpClient httpClient; @Test public void whenFetchingWeather() { when(httpClient.toBlocking().exchange(HttpRequest.GET("https://api.weatherapi.com/v1/current.json?key=XXX&q=city&aqi=no"), WeatherResponse.class)) .thenReturn(createWeatherResponse()); WeatherService weatherService = new WeatherService(); HttpResponse<WeatherResponse> response = weatherService.fetchWeather(HOUSE_NUM, STREET, POST_CODE, CITY); assertEquals(HttpStatus.OK, response.getStatus()); } public static HttpResponse<WeatherResponse> createWeatherResponse() { WeatherResponse.Condition condition = new WeatherResponse.Condition(); condition.setCode("1003"); WeatherResponse.CurrentWeather currentWeather = new WeatherResponse.CurrentWeather(); currentWeather.setCondition(condition); currentWeather.setWind("3.6"); currentWeather.setTemperature("2.0"); WeatherResponse weatherResponse = new WeatherResponse(); weatherResponse.setCurrentWeather(currentWeather); return HttpResponse.ok(weatherResponse); } }
解决方案
问题根源
只Mock了HttpClient实例,但未Mock它的toBlocking()方法返回的BlockingHttpClient对象,导致调用httpClient.toBlocking()时返回null,触发空指针异常。另外,直接new WeatherService()无法将Mock的HttpClient注入到服务类中,服务类实际使用的可能是未初始化的HttpClient实例。
修复步骤
- 添加
BlockingHttpClient的Mock实例,让httpClient.toBlocking()返回这个Mock对象 - 调整Mock顺序:先Mock
toBlocking()的返回值,再Mockexchange()方法 - 通过构造器或依赖注入将Mock的
HttpClient传入WeatherService
修复后的测试代码
@MicronautTest public class WeatherServiceTest { static final String HOUSE_NUM = "testNum"; static final String STREET = "testStreet"; static final String POST_CODE = "testPostCode"; static final String CITY = "testCity"; @Mock HttpClient httpClient; @Mock BlockingHttpClient blockingHttpClient; @Test public void whenFetchingWeather() { // 先Mock toBlocking()返回Mock的BlockingHttpClient when(httpClient.toBlocking()).thenReturn(blockingHttpClient); // 再Mock exchange方法的返回值 HttpRequest<?> request = HttpRequest.GET("https://api.weatherapi.com/v1/current.json?key=XXX&q=city&aqi=no"); when(blockingHttpClient.exchange(request, WeatherResponse.class)) .thenReturn(createWeatherResponse()); // 将Mock的httpClient传入WeatherService(假设服务类支持构造器注入) WeatherService weatherService = new WeatherService(httpClient); HttpResponse<WeatherResponse> response = weatherService.fetchWeather(HOUSE_NUM, STREET, POST_CODE, CITY); assertEquals(HttpStatus.OK, response.getStatus()); } public static HttpResponse<WeatherResponse> createWeatherResponse() { WeatherResponse.Condition condition = new WeatherResponse.Condition(); condition.setCode("1003"); WeatherResponse.CurrentWeather currentWeather = new WeatherResponse.CurrentWeather(); currentWeather.setCondition(condition); currentWeather.setWind("3.6"); currentWeather.setTemperature("2.0"); WeatherResponse weatherResponse = new WeatherResponse(); weatherResponse.setCurrentWeather(currentWeather); return HttpResponse.ok(weatherResponse); } }
额外说明
- 如果
WeatherService使用字段注入而非构造器注入,可通过@Inject或测试框架注入方式将Mock的HttpClient注入服务实例,避免直接new对象 - 建议使用参数匹配器(如
any(HttpRequest.class)、any(Class.class))提高Mock灵活性,避免因请求URL或参数细微变化导致Mock失效
内容的提问来源于stack exchange,提问作者Artishock
相关产品推荐
相关产品推荐

