#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. Спасибо! Ваш ответ был очень полезен! 🙂