【问题标题】:Error: java.lang.NullPointerException by RestTemplate exchange method - SpringBoot错误:RestTemplate 交换方法的 java.lang.NullPointerException - SpringBoot
【发布时间】:2021-10-19 09:58:28
【问题描述】:

我正在开发具有微服务架构的 Spring Boot 项目。我有一个通过 RestTemplate 与另一个服务通信的服务。 HttpDataClient.java 类正在向外部服务发送 dataId 并且应该收到一些响应。对于我的测试,我应该测试 RestTemplate 并检查我是否得到了良好的响应。

这是我需要测试的课程:

  public class HttpDataClient implements DataClient{
    
        private final static Logger LOGGER = LoggerFactory.getLogger(HttpDataClient.class);
    
        private final RestTemplate restTemplate;
        private final ObjectMapper objectMapper = new ObjectMapper();
    
        public HttpDataClient(RestTemplate restTemplate) {
            this.restTemplate = restTemplate;
        }
    
        @Override
        public DataResponse getData(String dataId) {
            try{
                JsonNode node = restTemplate.exchange(
                        String.format("/data/{0}", dataId),
                        HttpMethod.POST,
                        new HttpEntity<>(buildRequest(dataId), headers()),
                        JsonNode.class
                ).getBody();
                return dataResponse(node);
            }catch (HttpStatusCodeException e) {
                String msg = String.format(
                        "Error getting data for dataId: {0}",
                        dataId,
                        e.getStatusCode(),
                        e.getResponseBodyAsString());
                LOGGER.error(msg);
                return dataResponse.failed();
            }
        }
    
        private MultiValueMap<String, String> headers() {
            final LinkedMultiValueMap<String, String> mv = new LinkedMultiValueMap<>();
            mv.set(HttpHeaders.CONTENT_TYPE, "application/json");
            return mv;
        }
    
        private DataResponse dataResponse(JsonNode node) {
            return DataResponse.dataResponse(
                    asString(node, "dataId"),
                    asString(node, "author"),
                    asString(node, "authorDataId"),
                    asString(node, "serverSideDataId")
            );
        }
    
        private JsonNode buildRequest(String dataId) {
            ObjectNode root = objectMapper.createObjectNode();
            root.put("dataId", dataId);
            return root;
        }
    }

测试类如下所示:

@RunWith(MockitoJUnitRunner.class)
public class HttpDataServiceTest {

    @Mock
    RestTemplate restTemplate;

    @InjectMocks
    private HttpDataService httpDataService;

    @Test
    public void getData() {

        httpDataService.getData("gameIdTest");
        Mockito
            .when(restTemplate.exchange(
                    ArgumentMatchers.eq("/game/IdTest"),
                    ArgumentMatchers.eq(HttpMethod.POST),
                    ArgumentMatchers.any(),
                    ArgumentMatchers.<Class<DataResponse>>any()))
            .thenReturn(new ResponseEntity<>(HttpStatus.ACCEPTED));
    }
}

当我运行测试时,我得到一个 NullPointerException

java.lang.NullPointerException at com.example.gamedata.HttpDataService.getData(HttpDataService.java:37) at com.example.data.HttpDataServiceTest.getData(HttpDataServiceTest.java:36) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

我在这里错过了什么?

【问题讨论】:

  • 您的模拟场景与您的代码不匹配。 /game/idTest/ -> /data/{0} 这些完全不同,它返回 JsonNode 而不是 DataResponse。并且您的模拟应该在您实际调用该方法之前进行。
  • 你能否在我的代码中给我一个例子我不确定我是否理解。我应该在 HttpDataServiceClass 中用 DataResponse 更改 JsonNode 吗?
  • 为什么要更改代码?是你的测试是错误的(假设现在你的代码是正确的)。
  • 是的,是的,我就是这么想的。你有什么改变的建议?我是测试的初学者,所以我全神贯注:)
  • 对于初学者来说,最好阅读一本关于 Junit 和 Mockito 的书,而不是反复试验。假设您的代码是正确的,您的 mockito 模拟应该与该调用匹配,但目前不匹配。我已经在我的第一篇文章中解释了哪里出了问题,因此你需要改变什么。

标签: java spring-boot unit-testing resttemplate


【解决方案1】:

至少这些是错误的:

  • 您需要在调用实际方法之前执行 Mockito.when() 操作。不是之后。
  • /game/idTest//data/{0} 不同,它们不会匹配,但需要匹配才能使其正常工作
  • DataResponse 不是 JsonNode,它们也应该匹配
  • 在您的 when() 调用中,您实际上需要返回一些可在 HTTP 正文中接收的合理信息,仅“已接受”是不够的,它会使响应正文为空
  • 您需要提供一个合理的 json 节点作为响应

所以你的测试方法内容应该是这样的

    // create response object
    ObjectNode responseNode = JsonNodeFactory.instance.objectNode();
    responseNode.put("dataId", "");
    responseNode.put("author", "");
    responseNode.put("authorDataId", "");
    responseNode.put("serverSideDataId", "");
    
    // prepare your mock for the call
    Mockito
        .when(restTemplate.exchange(
                ArgumentMatchers.eq("/data/gameIdTest"),
                ArgumentMatchers.eq(HttpMethod.POST),
                ArgumentMatchers.any(),
                ArgumentMatchers.<Class<JsonNode>>any()))
        .thenReturn(new ResponseEntity<>(responseNode, HttpStatus.OK));

    // do the call
    httpDataService.getData("gameIdTest");

【讨论】:

  • 你能给我举个代码例子吗?我不确定我是否理解。
  • 那么在我的测试类的这行代码中,我应该把这个ArgumentMatchers.&lt;Class&lt;DataResponse&gt;&gt;any()))改成这个ArgumentMatchers.&lt;Class&lt;JsonNode&gt;&gt;any()))吗?
  • “你需要在调用实际方法之前做 Mockito.when() 的事情。不是之后。”我应该在Mockito.when()之后添加这行代码httpGameDataService.getGameData("gameIdTest");
  • "n 你的 when() 调用实际上需要返回一些合理的东西才能在 HTTP 正文中接收,只是 "Accepted" 是不够的,它使响应正文为空" 这一个
  • 谢谢!你的回答非常有帮助! :)
猜你喜欢
  • 2020-04-19
  • 2019-05-21
  • 2019-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 2017-10-07
相关资源
最近更新 更多