Spring Boot / Mockito: Mocking RestTemplate but Response Always Null - java

I'm having problems mocking the response object of my Test Class when using Mockito. I'm trying to test an exception, for this I need one of the attributes of the Class that returns from the POST request. I've successfully mocked the RestTemplate but my when().thenReturn() is not returning anything and I'm getting a null pointer exception at the "if" validation. If anyone could help me on this problem I would be very grateful.
Here is my Service Class:
#Service
public class CaptchaValidatorServiceImpl implements CaptchaValidatorService{
private static final String GOOGLE_CAPTCHA_ENDPOINT = "someEndpoint";
private String stage;
private String captchaSecret;
private RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
#Override
public void checkToken(String token) throws Exception{
MultiValueMap<String,String> requestMap = new LinkedValueMap<>();
requestMap.add("secret", captchaSecret);
requestMap.add("response", token);
try{
CaptchaResponse response = restTemplate.postForObject(GOOGLE_CAPTCHA_ENDPOINT,
requestMap, CaptchaResponse.class);
if(!response.getSuccess()){
throw new InvalidCaptchaTokenException("Invalid Token");
}
} catch (ResourceAccessException e){
throw new CaptchaValidationNotPossible("No Response from Server");
}
}
private SimpleClientHttpRequestFactory getClientHttpRequestFactory(){
...
}
}
And here is my Test Class:
#SpringBootTest
public class CaptchaValidatorTest{
#Mock
private RestTemplate restTemplate;
#InjectMocks
#Spy
private CaptchaValidatorServiceImpl captchaValidatorService;
private CaptchaResponse captchaResponse = mock(CaptchaResponse.class);
#Test
public void shouldThrowInvalidTokenException() {
captchaResponse.setSuccess(false);
Mockito.when(restTemplate.postForObject(Mockito.anyString(),
ArgumentMatchers.any(Class.class), ArgumentMatchers.any(Class.class)))
.thenReturn(captchaResponse);
Exception exception = assertThrows(InvalidCaptchaTokenException.class, () ->
captchaValidatorService.checkToken("test"));
assertEquals("Invalid Token", exception.getMessage());
}
}

In my opinion it could be a problem with ArgumentMatchers.
Method postForObject require parameters as String, MultiValueMap(or parent) and Class, but you set in Mockito.when: anyString() (correct), any(Class.class) (but MultiValueMap is passed - probably incorrect) and any(Class.class) (correct).
Try use:
Mockito.when(restTemplate.postForObject(ArgumentMatchers.any(String.class),
ArgumentMatchers.any(MultiValueMap.class), ArgumentMatchers.any(Class.class)))
.thenReturn(captchaResponse);
EDIT:
It seems to me that the CaptchaResponse in the test is unnecessarily a mock:
private CaptchaResponse captchaResponse = mock(CaptchaResponse.class);
but if You want this in that way, I think u need to replace:
captchaResponse.setSuccess(false);
to something like:
Mockito.when(captchaResponse.getSuccess()).thenReturn(false);

Related

How could I mock this and make sure to not get back null?

I have the following class that I want to unit test:
#RequiredArgsConstructor
#Service
public class Service {
private final WebClient.Builder builder;
private WebClient webClient;
#PostConstruct
public void init() {
searchUri = "/search-uri";
webClient = builder.baseUrl(searchUri)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
public ResponseSpec search() {
return webClient
.get()
.uri(uriBuilder ->
uriBuilder
.path("/search-uri")
// alot of query param, not important
.build()
)
.accept(MediaType.APPLICATION_JSON)
.acceptCharset(StandardCharsets.UTF_8)
.retrieve();
}
}
This is my test class:
#ExtendWith(MockitoExtension.class)
#RunWith(JUnitPlatform.class)
public class ServiceTest {
#InjectMocks
private Service service;
#Mock
private WebClient webClient;
#Mock
private Builder builder;
#Test
public void testSearch() {
when(builder.baseUrl(Mockito.anyString())).thenReturn(builder);
when(builder.defaultHeader(Mockito.anyString(), Mockito.anyString())).thenReturn(builder);
when(builder.build()).thenReturn(webClient);
issuerServiceImpl.init();
WebClient.RequestHeadersUriSpec uriSpecMock = mock(WebClient.RequestHeadersUriSpec.class);
WebClient.RequestHeadersSpec headersSpecMock = mock(WebClient.RequestHeadersSpec.class);
WebClient.ResponseSpec responseSpecMock = mock(WebClient.ResponseSpec.class);
when(webClient.get()).thenReturn(uriSpecMock);
lenient().when(uriSpecMock.uri(Mockito.any(URI.class))).thenReturn(headersSpecMock);
issuerServiceImpl.searchIssuers("");
}
}
An exception (NullPointerException) happens in the line of .accept(MediaType.APPLICATION_JSON) because the uri() returns null and the code tries to call method .accept() on a null object.
I am not let to change the Service class. I can change only my test class. Not sure how could I make it to work.
Edit: I made it to work, read answers.
So actually I changed this:
lenient().when(uriSpecMock.uri(Mockito.any(URI.class))).thenReturn(headersSpecMock);
to this:
lenient().when(uriSpecMock.uri(Mockito.any(Function.class))).thenReturn(headersSpecMock);
And it works good.
I think this is the problem
when(uriSpecMock.uri(Mockito.any(URI.class)))
you mocked uri to return something when it receives something that's URI class but you're using a lambda function.
The method signature is
S uri(Function<UriBuilder, URI> uriFunction);
Try changing it to
when(uriSpecMock.uri(Mockito.any(Function.class)))
Also check this for future use, quite a good article
https://www.baeldung.com/spring-mocking-webclient

JUnit/Mockito verifying `any(HttpPut.class)` however passes when other instances used too

I have a service class that calls a REST API to get, create, update and delete subscribers. The Uri remains the same, but the HTTP method changes as you'd expect. I want to test the correct method is given. Below is an example of the updateSubscriber and its test.
public class MyService {
HttpClient httpClient;
public MyService(HttpClient httpClient) {
this.httpClient = httpClient;
}
//...
public int updateSubscriber(Subscriber subscriber) throws ... {
// PUT is the correct method for this request
HttpResponse response = httpClient.execute( new HttpPut( "https://example.org/api/subscribers" ) );
//...
}
//...
Here is my test with JUnit and Mockito:
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest
{
#Mock
private HttpClient mockHttpClient;
#Mock
private HttpResponse mockResponse;
#Mock
private StatusLine mockStatusline;
#Mock
private HttpEntity mockEntity;
// test subject
private MyService myService;
#Before
public void setup() {
// // this will just ensure http* objects are returning our mocked instances so we can manipulate them..
// when(mockHttpClient.execute(any(HttpGet.class))).thenReturn(mockResponse);
// when(mockHttpClient.execute(any(HttpPost.class))).thenReturn(mockResponse);
// when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);
// when(mockHttpClient.execute(any(HttpDelete.class))).thenReturn(mockResponse);
// when(mockResponse.getStatusLine()).thenReturn(mockStatusline);
// when(mockStatusline.getStatusCode()).thenReturn(HttpStatus.SC_OK);
myService = new MyService(mockHttpClient);
}
#Test
public void testUpdateSubscriber() throws ...
{
when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);
when(mockResponse.getStatusLine()).thenReturn(mockStatusline);
when(mockStatusline.getStatusCode()).thenReturn(HttpStatus.SC_OK);
String responseString = "...";
// this is consumed by a static method which we cannot mock, so we must deal with an actual entity instance
BasicHttpEntity entity = new BasicHttpEntity();
entity.setContent(new ByteArrayInputStream(responseString.getBytes()));
when(mockResponse.getEntity()).thenReturn(entity);
// create a test case Subscriber instance
Subscriber subscriber = new Subscriber();
int statusCode = myService.updateSubscriber(subscriber);
assertEquals(HttpStatus.SC_OK, statusCode);
// just confirm that an HTTP request was made
// TODO this isn't working, still passes when wrong Http* method used
verify(mockHttpClient, times(1)).execute(any(HttpPut.class));
}
//...
However, when I (wrongfully) have the another Http* method instance, it still passes:
// this is wrong, and should fail, but passed :(
HttpResponse response = httpClient.execute( new HttpGet( "https://example.org/api/subscribers" ) );
I'd really like to be able to test this as the action performed could be wrong if the method is mistaken. This test is to ensure that the PUT method was correctly used with the HTTP request for updateSubscriber. Any ideas?
Test passes because HtppPut and HttpGet both are implementation classes of HttpRequestBase, Change the mocking from HttpRequestBase class to HttpPut class
when(mockHttpClient.execute(any(HttpPut.class))).thenReturn(mockResponse);
So now if you try with GET call Test will fail with NullPointerException since GET call has no stub
Not sure if this is the proper answer to my question but I got managed to get the tests to work as intended using a custom argument matcher:
package uk.ac.strath.matchers;
import org.apache.http.client.methods.HttpUriRequest;
import org.mockito.ArgumentMatcher;
public class HttpMethodMatcher implements ArgumentMatcher<HttpUriRequest> {
private String expectedClassName;
// constructors
public HttpMethodMatcher(String expectedClassName) {
this.expectedClassName = expectedClassName;
}
#Override
public boolean matches(HttpUriRequest httpMessage) {
if (httpMessage.getClass().getName().equals(expectedClassName)) {
return true;
}
return false;
}
}
Now in my test, I can do:
verify(mockHttpClient, times(1)).execute( argThat(new HttpMethodMatcher( HttpGet.class.getName() )) );
This tutorial was helpful: https://www.baeldung.com/mockito-argument-matchers

Spring Boot #Async annotation and MockRestServiceServer

I'm using Spring Boot 2.0.6 and Java 10. I did the following service that only hits an external rest api using RestTemplate.
#Service
#Slf4j
public class DbApiClientImpl implements DbApiClient {
private final String URL_DELIMITER = "/";
private RestTemplate restTemplate;
private String url;
public DbApiClientImpl(
RestTemplateBuilder restTemplate,
#Value("${dbapi.namespace}") String namespace,
#Value("${dbapi.url}") String uri,
#Value("${dbapi.username}") String username,
#Value("${dbapi.password}") String password) {
this.restTemplate = restTemplate.basicAuthorization(username,
password).build();
this.url = namespace.concat(uri);
}
#Override
#Async("asyncExecutor")
public Merchant fetchMerchant(String id) {
ResponseEntity<Merchant> response =
restTemplate.getForEntity(url.concat(URL_DELIMITER).concat(id),
Merchant.class);
return response.getBody();
}
}
And the following test using MockeRestServiceServer:
#RunWith(SpringRunner.class)
#RestClientTest(value = {DbApiClient.class})
public class DbApiClientTest {
private static final String TEST_NAME = "test";
private static final String TEST_NAME_BAD_REQUEST = "test-
1";
private static final String TEST_NAME_SERVER_ERROR =
"test-2";
#Autowired DbApiClient dbApiClient;
#Value("${dbapi.namespace}")
private String namespace;
#Value("${dbapi.url}")
private String dbApiUrl;
#Autowired private MockRestServiceServer mockServer;
#Autowired private ObjectMapper objectMapper;
#Test
public void test() throws
JsonProcessingException, IOException {
Merchant mockMerchantSpec = populateFakeMerchant();
String jsonResponse =
objectMapper.writeValueAsString(mockMerchantSpec);
mockServer
.expect(manyTimes(),
requestTo(dbApiUrl.concat("/").concat(TEST_NAME)))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(jsonResponse,
MediaType.APPLICATION_JSON));
assertNotNull(dbApiClient.fetchMerchant(TEST_NAME));
}
The thing is that I'm getting the following exception when I run the test "No further request expected HTTP GET http://localthost... excecuted"
So seems that the #Async is borking MockerServerService response...
Also, If I commented the #Async annotation everything works just fine and I get all test green.
Thanks in advance for your comments.
Update:
As per #M.Deinum's comment. I removed the CompletableFuture from the service but I'm still getting the same exception.
The problem is your code and not your test.
If you read the documentation (the JavaDoc) of AsyncExecutionInterceptor you will see the mention that only void or Future is supported as a return type. You are returning a plain object and that is internally treated as void.
A call to that method will always respond with null. As your test is running very quickly everything has been teared down already (or is in the process of being teared down) no more calls are expected to be made.
To fix, fix your method signature and return a Future<Merchant> so that you can block and wait for the result.
#Override
#Async("asyncExecutor")
public Future<Merchant> fetchMerchant(String id) {
ResponseEntity<Merchant> response =
restTemplate.getForEntity(url.concat(URL_DELIMITER).concat(id),
Merchant.class);
return CompletableFuture.completedFuture(response.getBody());
}
Now your calling code knows about the returned Future as well as the Spring Async code. Now in your test you can now call get on the returned value (maybe with a timeout to receive an error if something fails). TO inspect the result.

Mocking of Resttemplate failed in spring boot junit

I am writing junit test cases for the method which is call an rest api,following is the code I have tried:
#RunWith(MockitoJUnitRunner.class)
public class NotificationApiClientTests {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private NotificationApiClient notificationApiClient;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(notificationApiClient, "notificationUrl", "myURL***");
}
#Test
public void test_NotificationClickAPI_Call() throws JsonParseException, JsonMappingException, IOException {
ResponseEntity<NotificationClickEvent[]> notificationClickEventList = util.getValidNotificationEvent_ResponseEntity();
Mockito.when(restTemplate.exchange(
Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<NotificationClickEvent[]>> any()
)
).thenReturn(notificationClickEventList);
NotificationClickEvent[] notificationArray = notificationApiClient.requestNotificationClick(Const.NotificationClick, "2018-07-31-10");
assertTrue(notificationArray.length>0);
}
}
and in My NotificationApiClient , it was:
#Value("${notification.base.url}")
private String notificationUrl;
public NotificationApiClient() {
}
public UserInfoEvent[] requestUserInfo(String eventType, String dateStr) {
HttpEntity request = new HttpEntity(setHttpHeaders());
ResponseEntity<UserInfoEvent[]> response = this.exchange(
notificationUrl + eventType + "&dateStr=" + dateStr,
HttpMethod.GET, request, UserInfoEvent[].class);
UserInfoEvent[] userInfoRequest = response.getBody();
return userInfoRequest;
}
but it's not working, as per my code whenever the resttemplate.exchange method is called it should return the notificationClickEventList, But its calling the real api and returns the api result as the list.
Can anyone please help me to solve it?
In your code you are not using restTemplate.exchange method, It seems you are using notificationApiClient's exchange method. So try this.
#Spy
#InjectMocks
private NotificationApiClient notificationApiClient;
Mockito.when(notificationApiClient.exchange(
Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<NotificationClickEvent[]>> any()
)
).thenReturn(notificationClickEventList);

How do I mock a REST template exchange?

I have a service in which I need to ask an outside server via rest for some information:
public class SomeService {
public List<ObjectA> getListofObjectsA() {
List<ObjectA> objectAList = new ArrayList<ObjectA>();
ParameterizedTypeReference<List<ObjectA>> typeRef = new ParameterizedTypeReference<List<ObjectA>>() {};
ResponseEntity<List<ObjectA>> responseEntity = restTemplate.exchange("/objects/get-objectA", HttpMethod.POST, new HttpEntity<>(ObjectAList), typeRef);
return responseEntity.getBody();
}
}
How can I write a JUnit test for getListofObjectsA()?
I have tried with the below:
#RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
private MockRestServiceServer mockServer;
#Mock
private RestTemplate restTemplate;
#Inject
private SomeService underTest;
#Before
public void setup() {
mockServer = MockRestServiceServer.createServer(restTemplate);
underTest = new SomeService(restTemplate);
mockServer.expect(requestTo("/objects/get-objectA")).andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("{json list response}", MediaType.APPLICATION_JSON));
}
#Test
public void testGetObjectAList() {
List<ObjectA> res = underTest.getListofObjectsA();
Assert.assertEquals(myobjectA, res.get(0));
}
However the above code does not work, it shows that responseEntitty is null. How can I correct my test to properly mock restTemplate.exchange?
This is an example with the non deprecated ArgumentMatchers class
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
You don't need MockRestServiceServer object. The annotation is #InjectMocks not #Inject. Below is an example code that should work
#RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private SomeService underTest;
#Test
public void testGetObjectAList() {
ObjectA myobjectA = new ObjectA();
//define the entity you want the exchange to return
ResponseEntity<List<ObjectA>> myEntity = new ResponseEntity<List<ObjectA>>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(
Matchers.eq("/objects/get-objectA"),
Matchers.eq(HttpMethod.POST),
Matchers.<HttpEntity<List<ObjectA>>>any(),
Matchers.<ParameterizedTypeReference<List<ObjectA>>>any())
).thenReturn(myEntity);
List<ObjectA> res = underTest.getListofObjectsA();
Assert.assertEquals(myobjectA, res.get(0));
}
ResponseEntity<String> responseEntity = new ResponseEntity<String>("sampleBodyString", HttpStatus.ACCEPTED);
when(restTemplate.exchange(
Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<String>> any()
)
).thenReturn(responseEntity);
For me, I had to use Matchers.any(URI.class)
Mockito.when(restTemplate.exchange(Matchers.any(URI.class), Matchers.any(HttpMethod.class), Matchers.<HttpEntity<?>> any(), Matchers.<Class<Object>> any())).thenReturn(myEntity);
This work on my side.
ResourceBean resourceBean = initResourceBean();
ResponseEntity<ResourceBean> responseEntity
= new ResponseEntity<ResourceBean>(resourceBean, HttpStatus.ACCEPTED);
when(restTemplate.exchange(
Matchers.anyObject(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity> any(),
Matchers.<Class<ResourceBean>> any())
).thenReturn(responseEntity);
The RestTemplate instance has to be a real object. It should work if you create a real instance of RestTemplate and make it #Spy.
#Spy
private RestTemplate restTemplate = new RestTemplate();
I used to get such an error. I found a more reliable solution. I have mentioned the import statements too which have worked for me. The below piece of code perfectly mocks restemplate.
import org.mockito.Matchers;
import static org.mockito.Matchers.any;
HttpHeaders headers = new Headers();
headers.setExpires(10000L);
ResponseEntity<String> responseEntity = new ResponseEntity<>("dummyString", headers, HttpStatus.OK);
when(restTemplate.exchange( Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<String>> any())).thenReturn(responseEntity);
If anyone has this problem while trying to mock restTemplate.exchange(...), the problem seems to be with matchers. As an example: the following won't work,
when(ecocashRestTemplate.exchange(Mockito.any()
, Mockito.eq(HttpMethod.GET)
, Mockito.any(HttpEntity.class)
, Mockito.<Class<UserTransaction>>any())
).thenReturn(new ResponseEntity<>(transaction, HttpStatus.OK));
but this one will actually work:
ResponseEntity<UserTransaction> variable = new ResponseEntity<>(transaction, HttpStatus.OK);
when(ecocashRestTemplate.exchange(Mockito.anyString()
, Mockito.eq(HttpMethod.GET)
, Mockito.any(HttpEntity.class)
, Mockito.<Class<UserTransaction>>any())
).thenReturn(new ResponseEntity<>(transaction, HttpStatus.OK));
NOTICE the Mockito.anyString() on the second block vs theMockito.any().
Let say you have an exchange call like below:
String url = "/zzz/{accountNumber}";
Optional<AccountResponse> accResponse = Optional.ofNullable(accountNumber)
.map(account -> {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "bearer 121212");
HttpEntity<Object> entity = new HttpEntity<>(headers);
ResponseEntity<AccountResponse> response = template.exchange(
url,
GET,
entity,
AccountResponse.class,
accountNumber
);
return response.getBody();
});
To mock this in your test case you can use mocitko as below:
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<AccountResponse>>any(),
ArgumentMatchers.<ParameterizedTypeReference<List<Object>>>any())
)
When we are testing a Client which is communicating to some external system using restTemplate, as part of unit tests we need to verify the httpEntity, headers and parameters which we are sending.
ArgumentCaptor comes handy in these situation. So here is my example (working code)
#Mock
private RestTemplate restTemplate;
#InjectMocks
private MyClient client;
#Captor
ArgumentCaptor<HttpEntity<?>> httpEntityCaptor;
when(restTemplate.exchange(eq(expectedUrl), eq(HttpMethod.POST), Matchers.any(HttpEntity.class), eq(MyTargetResponse.class)).thenReturn(expectedResponse);
verify(restTemplate).exchange(eq(expectedUrl),eq(HttpMethod.POST), httpEntityCaptor.captor(),eq(MyTargetResponse.class));
HttpEntity<?> actualResponse = httpEntityCaptor.getValue();
HttpHeaders actualResponse.getHeaders();
assertEquals(headers.getFirst("Content-Type", "application/json")
Now assertions can be made based on your use case, as you have got the captured object which was sent.
You can use below non deprecated ArgumentMatchers
lenient().when(restTemplate.exchange(ArgumentMatchers.any(String.class),
ArgumentMatchers.eq(HttpMethod.GET),
ArgumentMatchers.any(),
ArgumentMatchers.eq(new ParameterizedTypeReference<List<ObjectA>>() {
})))
.thenReturn(responseEntity);
I implemented a small library that is quite useful. It provides a ClientHttpRequestFactory that can receive some context. By doing so, it allows to go through all client layers such as checking that query parameters are valued, headers set, and check that deserialization works well.
If you are using RestTemplateBuilder may be the usual thing wouldn't work. You need to add this in your test class along with when(condition).
#Before
public void setup() {
ReflectionTestUtils.setField(service, "restTemplate", restTemplate);
}
If anyone is still facing this issue, Captor annotation worked for me
#Captor
private ArgumentCaptor<Object> argumentCaptor;
Then I was able to mock the request by:
ResponseEntity<YourTestResponse> testEntity = new ResponseEntity<>(
getTestFactoryResponse(),
HttpStatus.OK);
when(mockRestTemplate.exchange((String) argumentCaptor.capture(),
(HttpMethod) argumentCaptor.capture(),
(HttpEntity<?>) argumentCaptor.capture(),
(Class<YourTestResponse.class>) any())
).thenReturn(testEntity);
For this specific exchange() case,
I found that easier just stub it instead, good old override:
var restTemplate = new RestTemplate() {
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, #Nullable HttpEntity<?> requestEntity,
Class<T> responseType) throws RestClientException {
throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
}
};
less mock stuff.. especially that api is always changing. eq(..) any().. etc.
you can check on arg inside of your stubbed exchange() before returning something or throwing exception.
--
I know that it is not the answer to that strict question. But same result. less code & easier to support.
With mockito-core-2.23.4
ResponseEntity<YOUR_CLASS> responseEntity = new ResponseEntity(YOUR_CLASS_OBJECT, HttpStatus.OK);
when(restTemplate.exchange(Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.<ParameterizedTypeReference<YOUR_CLASS>> any()))
.thenReturn(responseEntity);
Was get it working this way - Mockito.when(restTemplate.exchange((URI) any(), (HttpMethod) any(), (HttpEntity<?>) any(), (Class) any()))
.thenReturn(responseEntity);
However the above code does not work, it shows that responseEntitty is
null. How can I correct my test to properly mock
restTemplate.exchange?
It returns null because using:
#Mock
private RestTemplate restTemplate;
If the goal is to mock with MockRestServiceServer instead of Mockito, it should be:
#Autowired
private RestTemplate restTemplate;
or RestTemplate restTemplate = new RestTemplate()
when the same instance is provided to MockRestServiceServer and SomeService.
For example:
#Test
public void methodWithPostCallTest() throws URISyntaxException {
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
mockServer.expect(ExpectedCount.once(),
requestTo(new URI("post-method-url")))
.andExpect(method(HttpMethod.POST))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body("response-body")
);
YourService yourService = new YourService(restTemplate);
String response = yourService.methodWhichExecutesPostCall();
mockServer.verify();
assertEquals("response-body", response);
}
Using MockRestServiceServer, it would return mock response for any RestTemplate method for a POST call - postForEntity, postForObject or exchange.
More details: Testing Client Applications
If your intention is test the service without care about the rest call, I will suggest to not use any annotation in your unit test to simplify the test.
So, my suggestion is refactor your service to receive the resttemplate using injection constructor. This will facilitate the test. Example:
#Service
class SomeService {
#AutoWired
SomeService(TestTemplateObjects restTemplateObjects) {
this.restTemplateObjects = restTemplateObjects;
}
}
The RestTemplate as component, to be injected and mocked after:
#Component
public class RestTemplateObjects {
private final RestTemplate restTemplate;
public RestTemplateObjects () {
this.restTemplate = new RestTemplate();
// you can add extra setup the restTemplate here, like errorHandler or converters
}
public RestTemplate getRestTemplate() {
return restTemplate;
}
}
And the test:
public void test() {
when(mockedRestTemplateObject.get).thenReturn(mockRestTemplate);
//mock restTemplate.exchange
when(mockRestTemplate.exchange(...)).thenReturn(mockedResponseEntity);
SomeService someService = new SomeService(mockedRestTemplateObject);
someService.getListofObjectsA();
}
In this way, you have direct access to mock the rest template by the SomeService constructor.

Categories