Trying to mock restClient external API but it is invoking the actual API in java

1.2k Views Asked by At

I am trying to mock restClient external API but it is invoking the actual API instead of mocking it. Kindly help as I am not sure where I am going wrong.

I tried mocking the call and a few more other things but it didn't work.

public class TestService
{
    private static final String EXTERNAL_API = "http://LinktoExternalAPI/";

    @Autowired
    RestTemplate restTemplate;


    public  Map<String, String> countryCodes()
    {
        Map<String, String> map = new TreeMap<>();

        try
        {
            ResponseEntity<testCountry[]> responseEntity = restTemplate
                    .getForEntity(EXTERNAL_API
                            , testCountry[].class);
            List<testCountry> testCountryList = Arrays.asList(responseEntity.getBody());
            map = testCountryList.stream()
                    .collect(Collectors.toMap(testCountry::getCode, testCountry::getName));
        }

        catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc)
        {

        }

        return map;
    }
}

Test case for this is below:

@RunWith(PowerMockRunner.class)
public class TestServiceTest
{
    @InjectMocks
    TestService testService;

    @Mock
    RestTemplate restTemplate;

    private static final String EXTERNAL_API = "http://LinktoExternalAPI/";

    @Test
    public void testCountryCodes(){

        TestCountry testCountry = new TestCountry();
        testCountry.setCode("JPN");
        testCountry.setName("Japan");

        List<testCountry> testCountryList = new ArrayList<testCountry>();
        testCountryList.add(testCountry);

        Mockito.when(restTemplate.getForEntity(EXTERNAL_API, testCountry[].class)).thenReturn(new ResponseEntity(testCountryList, HttpStatus.OK));
        Map<String, String> result = testService.countryCodes();

        // result is pulling the actual size of the api instead of mocking and sending me testCountryList size.
        <Will mention assertion here>
    }

The result is pulling the actual size of the API instead of mocking and sending me testCountryList size.

2

There are 2 best solutions below

5
On

The reason behind the actual API being called is probably that the URL you are mocking is not exactly the same as that being generated at runtime, because of which a mock is not found and actual API is called. In these cases, you can use Mockito.any().

So the mock code will be Mockito.when(restTemplate.getForEntity(Mockito.any(), Mockito.any())).thenReturn(new ResponseEntity(testCountryList, HttpStatus.OK));

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {

  @InjectMocks
  private TestService testService;

  @Mock
  private RestTemplate restTemplate;

  @Test
  public void testCountryCodes(){

    TestCountry testCountry = new TestCountry();
    testCountry.setCode("JPN");
    testCountry.setName("Japan");

    TestCountry[] testCountryList = {
        testCountry
    };

    Mockito.when(restTemplate.getForEntity(Mockito.anyString(), Mockito.any())).thenReturn(new ResponseEntity(testCountryList, HttpStatus.OK));

    Map<String, String> result = testService.countryCodes();

    // result is pulling the actual size of the API instead of mocking and sending me testCountryList size.

  }
}

Also try using @RunWith(MockitoJUnitRunner.class) instead of PowerMockRunner.class since you don't seem to be needing the PowerMock capabilities.

0
On

You are mocking the wrong method definition.

A getForObject method with the parameters String and Class does not exist. You need to define behaviour for this method.

Note that in your case the third parameter (the varargs) is not used, so it defaults to an empty array. However Mockito requires this information to mock the correct call.

Mockito.when(restTemplate.getForObject(any(String.class), any(Class.class), ArgumentMatchers.<Object>any()))
       .thenReturn(result);

For a more complete example check my answer here.