[英]Mockito unit testing RestTemplate
I am using RestTemplate postForEntity
method to post body to an endpoint.我正在使用 RestTemplate postForEntity
方法将正文发布到端点。 I need help with writing test case for my code using Mockito.我需要帮助使用 Mockito 为我的代码编写测试用例。 The return type is void but it can be changed to Types
or code
if needed to test.返回类型为 void 但如果需要测试,可以更改为Types
或code
。 I have referred many other documentation but they are very general, I tried using them but most did not work for me as the request
and return type are different.我参考了许多其他文档,但它们非常通用,我尝试使用它们但大多数对我不起作用,因为request
和返回类型不同。 . . Any suggestions are appreciated.任何建议表示赞赏。 Thank you谢谢
Here is my Java class这是我的 Java 类
public void postJson(Set<Type> Types){
try {
String oneString = String.join(",", Types);
Map<String, String> requestBody = new HashMap<>();
requestBody.put("type", oneString);
JSONObject jsonObject = new JSONObject(requestBody);
HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
ResponseEntity result = restTemplate.exchange(url, HttpMethod.POST,
new HttpEntity<>(request, getHttpHeaders()), String.class);
}
}
}
You are testing the logic inside MyClass class, so you should not mock it.您正在测试 MyClass 类中的逻辑,因此不应模拟它。 RestTemplate
is a dependency inside MyClass, so this is exactly what you need to mock. RestTemplate
是 MyClass 中的一个依赖项,所以这正是你需要模拟的。 In general it should look like this inside your test:一般来说,它在你的测试中应该是这样的:
This is just a simple example.这只是一个简单的例子。 A good practice would be to check that the arguments passed to your mock equal to the expected ones.一个好的做法是检查传递给模拟的参数是否等于预期的参数。 One way would be to replace Mockito.eq()
with the real expected data.一种方法是用真实的预期数据替换Mockito.eq()
。 Another is to verify it separately, like this:另一种是单独验证,像这样:
public ResponseEntity<String> postJson(Set<Type> Types){
try {
String oneString = String.join(",", Types);
Map<String, String> requestBody = new HashMap<>();
requestBody.put("type", oneString);
JSONObject jsonObject = new JSONObject(requestBody);
HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
ResponseEntity result = restTemplate.exchange(url, HttpMethod.POST,
new HttpEntity<>(request, getHttpHeaders()), String.class);
}
}
return Types;
You can write test for above method as follows您可以为上述方法编写测试如下
@Mock
RestTemplate restTemplate;
private Poster poster;
HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), getHttpHeaders());
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.POST, request, String.class);
Mockito.verify(restTemplate, Mockito.times(1)).exchange(
Mockito.eq(uri),
Mockito.eq(HttpMethod.POST),
Mockito.eq(request),
Mockito.eq(String.class));
Assert.assertEquals(result, poster.postJson(mockData));
HttpHeaders getHttpHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add(// whatever you need to add);
return headers;
}
A while back, I wrote about unit testing and test doubles .不久前,我写了关于单元测试和测试替身的文章。 You can have a read as a starting point on how to approach unit testing.您可以将阅读作为如何进行单元测试的起点。
Some of it's key take aways are:其中一些关键要点是:
It's hard to write a whole test as a lot of information is missing.由于缺少大量信息,因此很难编写完整的测试。 Eg what Type
is.例如什么Type
是。 As you did not posted the name of your class I'm just name it MyClass
for now.由于您没有发布您班级的名称,因此我现在将其命名为MyClass
。 Also I'm assuming that the RestTemplate is injected via the constructor like另外我假设 RestTemplate 是通过构造函数注入的
MyClass(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
Following is a draft for a unit test using JUnit 5 and Mockito .以下是使用JUnit 5和Mockito的单元测试草案。 I would suggest mocking the RestTemplate
.我建议嘲笑RestTemplate
。 I have to admit that this way we will not cover to test the usage of MappingJackson2HttpMessageConverter
and StringHttpMessageConverter
in our test.不得不承认,这种方式我们不会覆盖测试MappingJackson2HttpMessageConverter
和StringHttpMessageConverter
在我们的测试中的使用。
So a very raw draft could look like this所以一个非常原始的草稿可能看起来像这样
import java.util.ArrayList;
import java.util.Set;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.http.HttpEntity;
import org.springframework.web.client.RestTemplate;
class MyClassTest {
private RestTemplate restTemplate;
private MyClass myClass;
@BeforeEach
void setUp() {
restTemplate = Mockito.mock(RestTemplate.class);
myClass = new MyClass(restTemplate);
}
@Test
void callMethod() {
Set<Type> types = Set.of(/* one of your Types */);
String url = "http://someUrl";
String httpResult = "";
Mockito.when(restTemplate.getMessageConverters()).thenReturn(new ArrayList<>());
ArgumentCaptor<HttpEntity> request = ArgumentCaptor.forClass(HttpEntity.class);
Mockito.when(restTemplate.postForObject(url, request.capture(), String.class)).thenReturn(httpResult);
myClass.callMethod(types, url);
HttpEntity<String> actualHttpEntity = request.getValue();
Assert.assertEquals(actualHttpEntity.getBody(), "");
}
}
Here is my solution to this problem, but it requires some changes.这是我对这个问题的解决方案,但它需要一些更改。
First of all, you have to externalize the RestTemplate
as a bean and add converters in its initialization.首先,您必须将RestTemplate
外部RestTemplate
bean 并在其初始化中添加转换器。 This way you will get rid of not covering those converters.通过这种方式,您将摆脱不覆盖这些转换器的情况。
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
restTemplate.getMessageConverters().add(jsonConverter);
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
restTemplate.getMessageConverters().add(stringConverter);
return restTemplate;
}
Here's the new class that contains postJson
method.这是包含postJson
方法的新类。 As you can see, the url
and restTemplate
are injected through the constructor.如您所见, url
和restTemplate
是通过构造函数注入的。 This way we can test different cases.这样我们就可以测试不同的情况。
public class Poster {
private RestTemplate restTemplate;
private String url;
public Poster(RestTemplate restTemplate, String url) {
this.restTemplate = restTemplate;
this.url = url;
}
public void postJson(Set<Type> types) {
try {
String oneString = types.stream().map(Type::toString).collect(Collectors.joining(","));
Map<String, String> requestBody = new HashMap<>();
requestBody.put("type", oneString);
requestBody.put("data", "aws");
JSONObject jsonObject = new JSONObject(requestBody);
HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), null);
ResponseEntity<String> result = restTemplate
.postForEntity(url, new HttpEntity<>(request, getHttpHeaders()), String.class);
int code = result.getStatusCodeValue();
} catch (Exception ignored) {}
}
private HttpHeaders getHttpHeaders() {
return new HttpHeaders();
}
}
And here's the test class for that method.这是该方法的测试类。
class PosterTest {
@Mock
private RestTemplate restTemplate;
private String url;
private Poster poster;
@BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
this.url = "http://example.com/posturl";
this.poster = new Poster(restTemplate, url);
}
@Test
void postJson() {
// set input, I used TreeSet just to have a sorted set
// so that I can check the results against
Set<Type> types = new TreeSet<>(Comparator.comparing(Type::toString));
types.add(new Type("a"));
types.add(new Type("b"));
types.add(new Type("c"));
types.add(new Type("d"));
// response entity
ResponseEntity<String> response = ResponseEntity.ok("RESPONSE");
// mockito mock
Mockito.when(restTemplate.postForEntity(
ArgumentMatchers.eq(url),
ArgumentMatchers.any(HttpHeaders.class),
ArgumentMatchers.eq(String.class)
)).thenReturn(response);
// to check the results
Map<String, String> requestBody = new HashMap<>();
requestBody.put("type", "a,b,c,d");
requestBody.put("data", "aws");
JSONObject jsonObject = new JSONObject(requestBody);
HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), null);
HttpEntity<HttpEntity<String>> httpEntity = new HttpEntity<>(request, new HttpHeaders());
// actual call
poster.postJson(types);
// verification
Mockito.verify(restTemplate, times(1)).postForEntity(
ArgumentMatchers.eq(url),
ArgumentMatchers.eq(httpEntity),
ArgumentMatchers.eq(String.class));
}
}
It is better to have the dependencies like RestTemplate
or other ServiceClass
es so that they can be mocked and tested easily.最好有RestTemplate
或其他ServiceClass
类的依赖RestTemplate
,以便可以轻松RestTemplate
和测试它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.