61

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?

Makoto
  • 104,088
  • 27
  • 192
  • 230
Akka Jaworek
  • 1,970
  • 4
  • 21
  • 47

19 Answers19

69

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);
Narm
  • 10,677
  • 5
  • 41
  • 54
itstata
  • 1,058
  • 7
  • 17
61

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));
    }
Mindaugas
  • 885
  • 9
  • 12
  • 2
    How do we add contents to the myEntity.getBody()? Mine comes back null – Angelina May 21 '19 at 21:12
  • 2
    Matchers are deprecated, you should use Mockito – Sham Fiorin May 22 '19 at 20:50
  • 3
    @Angelina since it's a real object just use an appropriate constructor i.e `ResponseEntity(T body, HttpStatus status)` – Mindaugas May 23 '19 at 05:56
  • @Mindaugas I´ve used the constructor but my response still is null ResponseEntity response = new ResponseEntity<>( "a string",HttpStatus.OK ); – Marino Oct 23 '19 at 14:43
  • 1
    @Marino make sure that your mocked method that is supposed to return the response entity is being called and that it is infact configured to return said entity – Mindaugas Oct 24 '19 at 13:23
  • I´m sorry, @Mindaugas but still doesn´t works! My test code is ResponseEntity responseEntity = new ResponseEntity<>("sampleBodyString", HttpStatus.CREATED); doReturn(responseEntity).when(restTemplate).exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(String.class)); TokenBean tokenBeanResult = tokenService.refresh("refresh-token"); and my real code is: ResponseEntity response = restTemplate.exchange(tokenApiConfig.getUrl(), HttpMethod.POST, request, String.class); and response is still null – Marino Oct 25 '19 at 11:24
  • @Marino is request parameter of type HttpEntity.class? cause that is what you are using to mock the rest template call – Mindaugas Nov 04 '19 at 15:02
  • 1
    The Matchers class is deprecated now – G_V Jun 22 '20 at 10:53
  • `@Mock` and `@InjectMocks` were a key in my test case. – Lanon Oct 21 '20 at 23:13
33
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);
  • Doesn't work with `org.hamcrest.Matchers` and `org.mockito.Matchers` is deprecated in favor of hamcrest. – Martin Schröder May 03 '18 at 16:20
  • 7
    "Matchers" is deprecated and should be replaced with "ArgumentMatchers" because of a name clash. [Class Matchers](https://static.javadoc.io/org.mockito/mockito-core/2.21.0/org/mockito/Matchers.html) – Roeland Van Heddegem Sep 11 '18 at 14:30
10

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);
Abbin Varghese
  • 2,422
  • 5
  • 28
  • 42
  • 1
    in my case: `when(restTemplate.exchange(any(String.class), eq(HttpMethod.GET), any(), eq(new ParameterizedTypeReference() {}))).thenReturn(myEntity);` – electrobabe Aug 31 '21 at 16:03
7

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);
chendu
  • 534
  • 5
  • 10
4

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();
Manoj Shrestha
  • 4,246
  • 5
  • 47
  • 67
2

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);
Dheeraj
  • 332
  • 3
  • 4
2

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().

lizom
  • 41
  • 7
2

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 headers = 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.

Sarneet Kaur
  • 2,860
  • 1
  • 14
  • 12
Sanjay Bharwani
  • 3,317
  • 34
  • 31
1

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())
)

Bhuwan Prasad Upadhyay
  • 2,916
  • 1
  • 29
  • 33
1

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);
Anand
  • 11
  • 1
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 18 '22 at 07:00
0

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.

Kier GRAY
  • 143
  • 2
  • 7
0

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);    
}
Denis Zavedeev
  • 7,627
  • 4
  • 32
  • 53
0

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);
Michael
  • 1
  • 1
0

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.

ses
  • 13,174
  • 31
  • 123
  • 226
0

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);
Ahmad Sayeed
  • 344
  • 2
  • 14
0

Was get it working this way - Mockito.when(restTemplate.exchange((URI) any(), (HttpMethod) any(), (HttpEntity<?>) any(), (Class) any())) .thenReturn(responseEntity);

  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 27 '22 at 02:11
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?

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

Justinas Jakavonis
  • 8,220
  • 10
  • 69
  • 114
-1

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.

Dherik
  • 17,757
  • 11
  • 115
  • 164