Ошибка: java.lang.Исключение NullPointerException методом обмена таблицами — SpringBoot

#java #spring-boot #unit-testing #resttemplate

Вопрос:

Я работаю над проектом Spring Boot с архитектурой микросервиса. У меня есть служба, которая связывается с другой службой через RestTemplate. HttpDataClient.java класс отправляет dataId внешнюю службу и должен получить что-то в ответ. Для моего теста я должен проверить эту табличку и проверить, получаю ли я хороший ответ.

Вот класс, который мне нужно протестировать:

   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)

Что я здесь упускаю?

Комментарии:

1. Ваш высмеянный сценарий не соответствует вашему коду. /game/idTest/ -> > /data/{0} они совершенно другие, и он возвращает JsonNode не DataResponse «а». И ваше издевательство должно начаться до того, как вы действительно вызовете метод.

2. Пожалуйста, приведите мне пример в моем коде, я не уверен, что понимаю. Должен ли я изменить JsonNode с помощью DataResponse в моем классе HttpDataServiceClass?

3. Почему вы должны изменить свой код? Это ваш тест неверен (предполагая, что на данный момент ваш код верен).

4. Да, да, именно так я и думал. Что вы посоветуете изменить? Я полный новичок в тестировании, поэтому я весь внимание 🙂

5. Ну для начала почитайте книгу о Джуните и Мокито вместо проб и ошибок. Предполагая, что ваш код верен, ваше издевательство над mockito должно соответствовать этому вызову, в настоящее время это не так. Я уже объяснил в своем первом посте, что было неправильно и, следовательно, что вам нужно изменить.

Ответ №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");
 

Комментарии:

1. Не могли бы вы привести мне пример в коде? Я не су ре, если я понимаю.

2. Итак, в этой строке кода в моем тестовом классе я должен изменить это ArgumentMatchers.<Class<DataResponse>>any())) на это ArgumentMatchers.<Class<JsonNode>>any())) ?

3. «вам нужно сделать Mockito.when (), прежде чем вызывать реальный метод. Не после.» должен ли я добавить эту строку кода httpGameDataService.getGameData("gameIdTest"); после Mockito.when()

4. «в вашем вызове when() вам действительно нужно будет вернуть что-то разумное, чтобы оно было получено в теле HTTP, просто «Принято» недостаточно, и оно оставляет тело ответа пустым» этот Извините, я полный новичок в тестировании

5. Спасибо! Ваш ответ был очень полезен! 🙂