Search code examples
spring-bootjunit4junit5

JUNIT - RestTemplate - Connecting to actual URL


Team,

My Service will run as Stand Alone program,

My Code:

public class TestRestTemplate {
    private RestTemplate restTemplate;

    public TestRestTemplate() {
        restTemplate = new RestTemplate();
    }

    public void identifyInvoiceCategory(final Properties properties) throws Throwable {
        final ResponseEntity<SearchResponse> exchange = restTemplate.exchange(
                properties.getProperty("ENDPOINT"), HttpMethod.GET,
                new HttpEntity<>(createSearchRequest(), createHttpHeader()), SearchResponse.class);

        final SearchResponse searchResponse = exchange.getBody();
        searchResponse.getEndIndex();
    }

    private SearchRequest createSearchRequest() throws Throwable {
        final SearchRequest searchRequest = new SearchRequest();
        return searchRequest;
    }

    private HttpHeaders createHttpHeader() {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        return httpHeaders;
    }
}

My Junit test Code


@RunWith(MockitoJUnitRunner.class)

public class TestRest {
    @Mock
    private RestTemplate restTemplate;

    @Test

    public void testIdentifyInvoiceCategoryValid() throws Throwable {
        try {
            Mockito.when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.eq(HttpMethod.GET),
                    ArgumentMatchers.any(), ArgumentMatchers.<Class<SearchResponse>>any())).thenReturn(null);
            new TestRestTemplate().identifyInvoiceCategory(createProperties());

        } catch (Throwable genThrowable) {
            genThrowable.printStackTrace();
        }
    }

    private Properties createProperties() {
        final Properties properties = new Properties();
        properties.setProperty("ENDPOINT", "http://www.test.com");
        return properties;
    }
}

When I run the JUNIT, I am getting the following exception


org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://www.test.com": Connection timed out: connect; nested exception is java.net.ConnectException: Connection timed out: connect
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:791)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:717)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:608)
    at com.gxs.orch.aggregator.util.TestRestTemplate.identifyInvoiceCategory(TestRestTemplate.java:23)

It looks like, it is trying to connect to the actual URL instead of Mocking...

Could you please help me to resolve this?


Solution

  • You mocked RestTemplate, but nowhere in your test you're actually injecting it into TestRestTemplate. Because of that, TestTestTemplate is using the RestTemplate you're creating in your constructor.

    The first step is to inject your mock in TestRestTemplate. The easiest way to do this is by using the @InjectMocks annotation:

    @RunWith(MockitoJUnitRunner.class)
    public class TestRest {
        // Add this field:
        @InjectMocks
        private TestRestTemplate testRestTemplate;
        @Mock
        private RestTemplate restTemplate;
    
        // ...
    }
    

    After that, you have to refactor your tests so that they use the TestRestTemplate instance created by Mockito rather than using your own. This means that everywhere you use new TestRestTemplate(), you now refer to the testRestTemplate field.

    For example:

    @Test
    public void testIdentifyInvoiceCategoryValid() throws Throwable {
        // ...
        // Don't use new TestRestTemplate():
        testRestTemplate.identifyInvoiceCategory(createProperties());
        // ...
    }
    

    Also, while it's not really necessary for @InjectMocks to work, it's considered a bad practice to create a new RestTemplate instance within the constructor of TestRestTemplate because it increases coupling. To solve this, you can refactor your constructor to accept an external RestTemplate instance:

    public class TestRestTemplate {
        private RestTemplate restTemplate;
    
        // Refactor your constructor to this:
        public TestRestTemplate(RestTemplate restTemplate) {
            this.restTemplate = restTemplate;
        }
    
        // ...
    }
    

    Some other recommendations:

    • If you want to use JUnit 5 (you tagged it with ), you should use @ExtendWith(MockitoExtension.class) rather than using @RunWith().
    • RestTemplate has several overloaded methods that achieve the same thing: to call a REST API. Ideally you shouldn't rely in your tests on the right overloaded method being called. In stead of that, you should verify whether the right API call was made, regardless of what RestTemplate method you use. For that purpose, you can use MockRestServiceServer.