Search code examples
mavenunit-testingspring-bootresttemplatespring-test

How to mock resttemplate calls in spring boot?


I tried to write test cases for the rest calls in my service which is calling the 3rd party api.

@RunWith(MockitoJUnitRunner.class)
public class ForceServiceTest {
private ForceService forceService;
@Mock
private ForceServiceConfig config;
@Mock
private RestTemplate restTemplate;

@Before
public void setup() {
    forceService = new ForceService(config);
}

@Test
public void apiCall_valid() throws JSONException {
    HttpHeaders headers = new HttpHeaders();
    headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
    headers.set(ACCEPT, APPLICATION_JSON);
    HttpEntity<String> entity = new HttpEntity<>(
            "id=null",
            headers);
    config.authTokenUrl = "https://ex...com/..";
    Mockito.when(restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity, Access.class)).thenReturn(null);
    // Mockito.when(any()).thenReturn(null);
    forceService.apiCall();
}

}

@Component
public class ForceService {
    private ForceServiceConfig config;
    private RestTemplate restTemplate = new RestTemplate();
public ForceService(ForceServiceConfig config) {

    this.config = config;
}
    private String apiCall() {
    HttpHeaders headers = new HttpHeaders();
    headers.set(CONTENT_TYPE, "application/x-www-form-urlencoded");
    headers.set(ACCEPT, APPLICATION_JSON);

    HttpEntity<String> entity = new HttpEntity<>(
            "&id=" + config.id,
            headers);
    ResponseEntity<Access> response = restTemplate.exchange(config.authTokenUrl, HttpMethod.POST, entity,
            Access.class);
    return response.getBody().token_type + " " + response.getBody().access_token;
     }

}

I get the following error:

org.springframework.web.client.HttpClientErrorException: 404 Not Found at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:78) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)

It is calling the api in test class, which I do not want to happen. I need to mock the resttemplate call of 3rd party api. How can I do it without being actually calling the api?


Solution

  • This is the problem

    public class ForceService {
        private ForceServiceConfig config;
        private RestTemplate restTemplate = new RestTemplate(); // HERE
    

    you are creating new REAL rest template. What you probably want is to

    1. Use mock that you created in wrapping test class
    2. Use real rest template and SPY on it.

    A don't know is that your actualy structure (1 file 2 classes) but it is safe to assume it is not and in any case you can simply pass RestTemplate as ctor argument. So

    @Component
    public class ForceService {
        private ForceServiceConfig config;
        private RestTemplate restTemplate;
    public ForceService(ForceServiceConfig config,RestTemplate restTemplate) {
        this.restTemplate=restTemplate;
        this.config = config;
    }
    

    and

    @Before
    public void setup() {
        forceService = new ForceService(config,restTemplate);
    }
    

    Now if you want to RestTemplate to be just a stub that does literally nothing and return null on any calls if not instructed otherwiser - leave it as @Mock.

    If you want however, to allow it to work normally and only intercept some specific method calls and stub responses, use spy.

    @Mock
    private RestTemplate restTemplate;
    private RestTemplate restTemplate=Mockito.mock(RestTemplate.class)
    

    or

    private RestTemplate restTemplate=Mockito.spy(new RestTemplate());