4

I'm having a problem with Mockito where the real method is being called in the test rather than the mocked method. Searched for hours but haven't been able to find a suitable answer.

Here's the service I'm testing:

@Service public class DwpApiService { @Autowired private RestTemplate restTemplate = new RestTemplate(); @Bean public RestTemplate restTemplate() { return new RestTemplate(); } private GeoService geoService = new GeoService(); private String baseUri = "XXXX"; private double cityLatt = 51.5074; private double cityLong = -0.1278; public List<PersonApiModel> getAllUsersInCityOrWithinDistanceOfCity(String cityName, double distanceInMiles) throws RuntimeException { HashMap<Integer, PersonApiModel> usersInCityOrWithinDistance = new HashMap<>(); List<PersonApiModel> usersInCity = getAllUsersInCity(cityName).getBody(); for (PersonApiModel person: usersInCity) { usersInCityOrWithinDistance.put(person.getId(), person); } List<PersonApiModel> allUsers = getAllUsers().getBody(); for (PersonApiModel person : allUsers) { boolean withinDistance = geoService.isLocationWithinDistance(distanceInMiles, cityLatt, cityLong, person); if (withinDistance && !usersInCityOrWithinDistance.containsKey(person.getId())) { usersInCityOrWithinDistance.put(person.getId(), person); } } return new ArrayList<>(usersInCityOrWithinDistance.values()); } public ResponseEntity<List<PersonApiModel>> getAllUsersInCity(String cityName) throws RuntimeException { String cap = cityName.substring(0, 1).toUpperCase() + cityName.substring((1)); if (!cap.equals("London")) { throw new IllegalArgumentException("Invalid city name. Please only use the city of London."); } return restTemplate.exchange( baseUri + "city/" + cap + "/users", HttpMethod.GET, null, new ParameterizedTypeReference<List<PersonApiModel>>(){} ); } public ResponseEntity<List<PersonApiModel>> getAllUsers() throws RuntimeException { return restTemplate.exchange( baseUri + "/users", HttpMethod.GET, null, new ParameterizedTypeReference<List<PersonApiModel>>(){} ); } } 

This service calls the following GeoService class:

public class GeoService { //taken from https://www.movable-type.co.uk/scripts/latlong.html public boolean isLocationWithinDistance(double radiusInMiles, double sourceLat, double sourceLong, PersonApiModel personApiModel) throws IllegalArgumentException { boolean isLatLongWithinRange = checkIfLatLongAreWithinRange( sourceLat, sourceLong, personApiModel.getLatitude(), personApiModel.getLongitude()); if (!isLatLongWithinRange) { throw new IllegalArgumentException("Latitude or Longitude are not valid values for id=" + personApiModel.getId()); } int earthMeanRadius = 6371; double latDiff = Math.toRadians(sourceLat - personApiModel.getLatitude()); double longDiff = Math.toRadians(sourceLong - personApiModel.getLongitude()); double a = Math.sin(latDiff / 2) * Math.sin(latDiff / 2) + Math.cos(Math.toRadians(sourceLat)) * Math.cos(Math.toRadians(personApiModel.getLatitude())) * Math.sin(longDiff / 2) * Math.sin(longDiff / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); double distance = earthMeanRadius * c; double radiusInKm = radiusInMiles * 1.609344; return radiusInKm >= distance; } public boolean checkIfLatLongAreWithinRange(double sourceLat, double sourceLong, double destinationLat, double destinationLong) { boolean sourceLatInRange = Math.abs(sourceLat) <= 90; boolean destLatInRange = Math.abs(destinationLat) <= 90; boolean sourceLongLessThanMinus180 = sourceLong >= -180; boolean sourceLongLessThan80 = sourceLong <= 80; boolean destLongLessThanMinus180 = destinationLong >= -180; boolean destLongLessThan80 = destinationLong <= 80; return sourceLatInRange && destLatInRange && sourceLongLessThanMinus180 && sourceLongLessThan80 && destLongLessThanMinus180 && destLongLessThan80; } } 

My test case looks like this:

@RunWith(MockitoJUnitRunner.class) public class DwpApiServiceTest { @Mock private RestTemplate restTemplate; @Mock private GeoService geoService; @InjectMocks private final DwpApiService dwpApiService = new DwpApiService(); private final PersonApiModel personApiModel1 = new PersonApiModel(); private final PersonApiModel personApiModel2 = new PersonApiModel(); private final List<PersonApiModel> fakeList1 = new ArrayList<>(); private final List<PersonApiModel> fakeList2 = new ArrayList<>(); private final String baseUri = "XXXXXX"; private double cityLatt = 51.5074; private double cityLong = -0.1278; @Test public void givenMocks_whenGetAllUsersInAndAroundCity_returnMockedPersonList() { personApiModel1.setId(1); fakeList1.add(personApiModel1); ResponseEntity<List<PersonApiModel>> expected1 = new ResponseEntity<>(fakeList1, HttpStatus.OK); personApiModel2.setId(2); fakeList2.add(personApiModel2); ResponseEntity<List<PersonApiModel>> expected2 = new ResponseEntity<>(fakeList2, HttpStatus.OK); PersonApiModel person = personApiModel2; List<PersonApiModel> expected = new ArrayList<>(); expected.addAll(fakeList1); expected.addAll(fakeList2); String cityName = "london"; double distanceInMiles = 20; Mockito.when(dwpApiService.getAllUsers()) .thenReturn(expected1); Mockito.when(dwpApiService.getAllUsersInCity(cityName)) .thenReturn(expected2); Mockito.when(geoService.isLocationWithinDistance(distanceInMiles, cityLatt, cityLong, person)) .thenReturn(true); List<PersonApiModel> actual = dwpApiService.getAllUsersInCityOrWithinDistanceOfCity(cityName, distanceInMiles); Assert.assertEquals(expected, actual); } 

Now, mocking the DwiApiService works fine, with this test case and others. But mocking the GeoService seems to cause issue. When I try to run the test, it does not actually invoke the mocked method but the real one instead. The errors aren't very helpful...

[MockitoHint] DwpApiServiceTest.givenMocks_whenGetAllUsersInAndAroundCity_returnMockedPersonList (see javadoc for MockitoHint): [MockitoHint] 1. Unused... -> at com.dwpAPI.services.DwpApiServiceTest.givenMocks_whenGetAllUsersInAndAroundCity_returnMockedPersonList(DwpApiServiceTest.java:115) [MockitoHint] ...args ok? -> at com.dwpAPI.services.DwpApiService.getAllUsersInCityOrWithinDistanceOfCity(DwpApiService.java:47) 

Does anybody know what's going on here? Spending hours and hours on this and just can't seem to figure it out.

4
  • Try autowiring geoService instead of initializing with new. Commented Jun 14, 2020 at 17:25
  • Tried that, getting the same error. Commented Jun 14, 2020 at 17:28
  • Does this answer your question? Mockito - difference between doReturn() and when() Commented Jun 14, 2020 at 18:09
  • @Progman Tried that but didn't seem to work. Stepio below found an answer that works. Not entirely sure why though! Commented Jun 14, 2020 at 18:10

2 Answers 2

1

Possible issue is that you specify exact parameters. Consider mockito configuration as set of rules, so exact values don't work there (unless you're really lucky and it just magically works for some specific narrow case).

So try replacing this one:

when(geoService.isLocationWithinDistance(distanceInMiles, cityLatt, cityLong, person)) .thenReturn(true); 

With this:

when(geoService.isLocationWithinDistance(eq(distanceInMiles), eq(cityLatt), eq(cityLong), eq(person))) .thenReturn(true); 

Also I sometimes have issues using when(...).thenReturn(...) construction - it does not work properly with @Spy, for example. So I usually prefer this approach instead:

doReturn(true).when(geoService) .isLocationWithinDistance(eq(distanceInMiles), eq(cityLatt), eq(cityLong), eq(person)); 

And if some of the parameters is not really important for you (or all of them), don't use eq(...) and don't use any value - just replace the relevant parameter with any().

P.S.: I changed just a single mocked rule, assuming that you'll review others yourself.

Sign up to request clarification or add additional context in comments.

1 Comment

Hmmm I replaced when(geoService.isLocationWithinDistance(distanceInMiles, cityLatt, cityLong, person)).thenReturn(true); with when(geoService.isLocationWithinDistance(anyDouble(), anyDouble(), anyDouble(), any())).thenReturn(true); and it worked! Not sure why, so will dig into it a bit deeper. Thanks!
0

the reason may be cause u are directly instanciance it

private GeoService geoService = new GeoService(); 

you probably need to annotated the class GeoService with @Service and autowired it on DwpApiService

1 Comment

Added the annotation @Service to the GeoService class and @Autowired to the GeoService reference in DwiApiService class. Still getting the same error.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.