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);
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.
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);
I have the following implementation of class A, using spring boot.
A is an abstraction over restTemplate to make GET/POST/PUT RestAPI calls.
Tests are written using Mockito.
Class A {
#Bean
RestTemplate restTemplate;
public class A(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
public ResponseEntity perform(String endPoint, String requestBody, String auth, HttpMethod httpMethod){
...code to create parameters to pass to the exchange method
ResponseEntity responseEntity = restTemplate.exchange(url, httpMethod, requestEntity, String.class)
return responseEntity;
}
}
Unit Test With Mockito:
class ATest{
A a;
#Spy
RestTemplate restTemplate;
#Before
public void setUp() {
a = Mockito.spy(new A(restTemplate));
}
#Test
public void testHttpUtil(){
ResponseEntity responseEntity = new ResponseEntity(HttpStatus.OK);
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization" , "testAuth");
HttpEntity<String> requestEntity = new HttpEntity<String>("testPayload", headers);
Mockito.doReturn(responseEntity).when(restTemplate).exchange(Mockito.any(),Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.eq(String.class));
ResponseEntity responseEntity1 = a.perform("https://example.com/v1/testapi", "testpayload", "testauthinfo", MediaType.APPLICATION_JSON_VALUE, HttpMethod.POST );
Assert.assertNotNull(responseEntity1);
Mockito.verify(restTemplate, Mockito.atMost(1)).exchange(Mockito.any(),Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.eq(String.class));
}
}
My thought behind this implementation is to mock the restTemplate's exchange method, and return a response, upon calling A's perform method.
Right now null is being returned upon A's perform method.
Looks like I am doing something wrong. Can someone pls assist me on this?
To circumvent this, I mocked the RestTemplate and used
Mockito.verify(restTemplate, Mockito.atMost(1)).exchange(Mockito.any(),Mockito.eq(HttpMethod.POST), Mockito.any(HttpEntity.class), Mockito.eq(String.class));
to find out how many times restTemplate.exchange method has been called. Tests are passing now. But still want to know what's wrong with the implementation posted in the question
Update: On instantiating the restTemplate, the test passed. Did the following change to the test code.
#Spy
RestTemplate restTemplate = new RestTemplate();
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.