Mocked rest template exchange method returning null - java

I am trying to mock an exchange call from a rest template but for some reason i am getting a null response from the call rather than the response entity i have specified in my test. Note - before adding the injectMocks on my service interface the rest template was trying to make an actual call, when i added that in it makes a mock call but with a null result.
#ActiveProfiles("unit-test")
#RunWith(SpringJUnit4ClassRunner.class)
#Category({ UnitTests.class })
#SpringBootTest#Import({PropertiesTestConfiguration.class})
public class MyTest {
#Mock
OAuth2RestTemplate serviceRestTemplate;
#Autowired
#InjectMocks
ServiceInterface serviceInterface;
#Test
public void getServiceResponse_Success() {
ResponseEntity<String> mockResponseEntity = new ResponseEntity<String>(mockResponseBody, HttpStatus.OK);
String url = "https://unit_test_/XXX";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
Mockito.when(serviceRestTemplate.exchange( Matchers.anyObject(), Matchers.any(HttpMethod.class), Matchers.<HttpEntity> any(), Matchers.<Class<String>> any()) ).thenReturn(mockResponseEntity);
ServiceInterface.getClaimByClaimId(XXX);
}
}
In the method I am testing this returns null
responseEntity = serviceRestTemplate.exchange(uriBuilder.toUriString(),
method, requestEntity, String.class);

If you are using hamcrest matchers i will recommed to use is and isA methods to match the value or instance, this artical explains more about core matchers
Mockito.when(serviceRestTemplate.exchange(is(instanceOf(String.class)),
is(HttpMethod.GET),
is(HttpEntity.class),
isA(String.class)))
.thenReturn(mockResponseEntity);

Related

Mock WebClient.ResponseSpec to test external api call in Junit integration testing

How to mock WebClient.ResponseSpec, expecting response in pojo class list. code as below
public ResponseSpec externalAPICall(RequestBody requestBody) {
//ExternalApi
String url = "http://dev1:8080/external/api";
return client.post().uri(url).contentType(MediaType.APPLICATION_JSON)
.body(Mono.just(requestBody), RequestBody.class).retrieve();
}
tried to mock ResponseSpec with body, but getting NullPointerException

Spring Boot integration test. How to test the 'DELETE' method?

I'm using Spring Boot 2.5.6 and JUnit 4.13.2. My task is to test the DELETE method
My REST controller:
#RestController
public class DomainEndpoint {
private final SomeService service;
#DeleteMapping("/domain/{id}")
public void delete(#PathVariable long id) {
service.delete(id);
}
}
My test:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#RunWith(SpringRunner.class)
public class DomainEndpointTest {
#Autowired
TestRestTemplate template;
#MockBean
SomeService service;
#Test
public void delete() {
String url = "/domain/123";
ResponseEntity<?> resp = template.exchange(url, HttpMethod.DELETE, new HttpEntity<>(""), String.class);
assertEquals(HttpStatus.NO_CONTENT, resp.getStatusCode());
}
}
As you can see, the only solution for testing the 'DELETE' method, which I found, is:
ResponseEntity<?> resp = template.exchange(url, HttpMethod.DELETE, new HttpEntity<>(""), String.class);
But params for body new HttpEntity<>("") and for return type String.class seem strange to me. Why should I use them? Can I do the same more straightway without passing unnecessary parameters?
On the other hand, TestRestTemplate template has a set of short and readable methods delete(). The problem with them - they return void and I can't check the response status code in this case.
The main question is how to test DELETE methods correctly?
Instead of passing in new HttpEntity<>("") HttpEntity has a special class variable you can use called HttpEntity.EMPTY for these situations. You can also use a return type of void.
ResponseEntity<Void> resp = template.exchange(url, HttpMethod.DELETE, HttpEntity.EMPTY, Void.class);
Two things you could improve:
Don't provide a request entity – it is marked as #Nullable
Use Void.class as return type to express that you don't expect any response body
ResponseEntity<Void> resp = restTemplate.exchange(url, HttpMethod.DELETE, null, Void.class);
Others have given different options but if you want to use the the delete method - it internally handles the http error by throwing runtime error as you can see here. You can check for HttpClientErrorException or HttpServerErrorException by using assertDoesNotThrow from JUnit5
In my opinion RestTemplate.delete method is the one to use, return type is void but status other than 2XX will throw an exception.

Mocked object returns null

JUnit test class :
public class TestingClass {
#Mock
private RestTemplate restTemplate;
#Mock
private HttpEntity entity;
#Mock
private ResponseEntity<Resource> responseEntity;
#Before
public void setup() {
MockitoHelper.initMocks(this);
}
#Test
public void getDataTest() {
ClassToTest c = new ClassToTest(restTemplate);
when(restTemplate.exchange("http://testing", HttpMethod.GET, entity, Resource.class)).thenReturn(responseEntity);
c.getData("http://testing");
}
}
Class being tested :
import org.jsoup.helper.Validate;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.io.InputStream;
import java.util.Optional;
public class ClassToTest {
private HttpHeaders headers;
private RestTemplate restTemplate;
public ClassToTest(RestTemplate restTemplate){
this.restTemplate = restTemplate;
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
}
public Optional<InputStream> getData(final String mediaUrl) {
Validate.notEmpty(mediaUrl);
final String requestJson = "{}";
final HttpEntity<String> entity = new HttpEntity<>(requestJson, headers);
Optional inputStreamOptional = Optional.empty();
try {
final ResponseEntity<Resource> responseEntity = restTemplate.exchange(mediaUrl, HttpMethod.GET, entity, Resource.class);
System.out.println(responseEntity);
} catch (Exception exception) {
exception.printStackTrace();
}
return inputStreamOptional;
}
}
The result of System.out.println(responseEntity); is null .
Should responseEntity be set to it's mocked value and returned (instead of current behavior where null is returned) as is configured in : when(restTemplate.exchange("http://testing", HttpMethod.GET, entity, Resource.class)).thenReturn(responseEntity);
So when c.getData("http://testing");is invoked the mocked responseEntity is returned ?
Update use instead :
when(restTemplate.exchange(Matchers.eq("http://testing"), Matchers.eq(HttpMethod.GET), Matchers.isA(HttpEntity.class), Matchers.eq(Resource.class))).thenReturn(responseEntity);
It's most likely returning null because your parameter definition in when and the actual parametes differ. In your case it's very likely that your mocked entity and the HttpEntity you're creating in your code under test are neither the same nor equal. So you need to widen your expectations in the when-definition. You should use Matchers in your definition and the could use isA(HttpEntity.class) for your entity.
I think that you don't need to mock ResponseEntity, since ResponseEntity is not the one being injected. You mocked ResponseEntity and then never mocked any of its methods , that's why its null.
You need to mock RestTemplate and then mock its:
when(restTemplate.exchange("http://testing", HttpMethod.GET, entity, Resource.class)).thenReturn(responseEntity);
The problem is centered around this line in your test:
when(restTemplate.exchange("http://testing", HttpMethod.GET, entity, Resource.class)).thenReturn(responseEntity);
What this says is to return your mock responseEntity when you call restTemplate.exchange with these specific arguments. Notice that one of those arguments is your mock entity. However, in your ClassToTest, that mock entity is not what's passed into restTemplate.exchange. So when you run your test, you never hit the exact method signature expected by your stub.
Try this, instead:
when(restTemplate.exchange(eq("http://testing"), eq(HttpMethod.GET), any(HttpEntity.class), eq(Resource.class))).thenReturn(responseEntity);
This replaces the specific parameters with matchers, and will cause your stubbed method to trigger on any HttpEntity rather than the specific mock one.
Note that if you use any matchers at all, you have to use matchers for all the parameters, which is what the eq() method is for.

Mocking Spring restTemplate.exchange call with parameterized reference type argument

I am trying to mock a call to RestTemplate.exchange() but cant get it to work. Currently the call to exchange() hangs so I believe the actual method is being called instead of my mock. The call to exchange() is as follows:
ResponseEntity<List<MyType>> response =
restTemplate.exchange(queryStr,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<MyType>>() {
});
The mocking is as follows:
#MockBean
private RestTemplate restTemplate;
#Test
public void testMethod() throws Exception {
when(restTemplate.exchange(anyString(),
eq(HttpMethod.GET),
eq(null),
eq(new ParameterizedTypeReference<List<MyType>>(){})
)).thenReturn(new ResponseEntity<List<MyType>>(HttpStatus.OK));
// rest of test code follows.
}
I have tried changing the argument matchers around so they match a broader argument types (ie. any() in place of anyString()) but I get the same behavior or an error "reference to exchange is ambiguous both method exchange(...) and method exchange(...) match". I also get "no suitable method found for thenReturn(...) is not compatible with thenReturn(...)" along with the first error.
Thanks in advance.
Found that we did not annotate the instance of the RestTemplate with #Autowired that was used in our controler.
#RestController
public class myController {
...
#Autowired // <-- Forgot this annotation.
private RestTemplate restTemplate;
...
}
Now mocks work correctly.

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