8

In my unit test i want to mock the interaction with elasticsearch by doing the following

when(cityDefinitionRepository.findCitiesNearby(geoPoint, SOURCE, 2)).thenReturn(cityDefinitionsArrival);
when(cityDefinitionRepository.findCitiesNearby(geoPoint2, SOURCE, 2)).thenReturn(cityDefinitionsDeparture);
SearchResult results = benerailService.doSearch(interpretation, 2, false);

the doSearch method contains

departureCityDefinitions = cityDefinitionRepository.findCitiesNearby(geo, SOURCE, distance);

When i debug my code i see that mockito is called in my doSearch method but it does not return the cityDefinitionsArrival object. This is probably because geoPoint and geo are two different objects.

The geoPoint and geo object are both elasticsearch GeoPoints which contain the same latitude and longitude.

I managed to get this to work by doing

when(cityDefinitionRepository.findCitiesNearby(any(geoPoint.getClass()), eq(SOURCE), eq(2))).thenReturn(cityDefinitionsArrival);
when(cityDefinitionRepository.findCitiesNearby(any(geoPoint2.getClass()), eq(SOURCE), eq(2))).thenReturn(cityDefinitionsDeparture);

But now it ignores my latitude and longitude values and accepts any object of the GeoPoint class. This is a problem because in my doSearch method i have two usages of the findCitiesNearby, each with different latitude and longitude and I need to mock them both individually.

Is this possible with Mockito?

The cityDefinitionsArrival and cityDefinitionsDeparture are both ArrayLists, the SOURCE is a String value and the geo and geoPoint object:

GeoPoint geoPoint = new GeoPoint(50.850449999999995, 4.34878);
GeoPoint geoPoint2 = new GeoPoint(48.861710, 2.348923);

double lat = 50.850449999999995;
double lon = 4.34878;
GeoPoint geo = new GeoPoint(lat, lon);

double lat2 = 48.861710;
double lon2 = 2.348923;
GeoPoint geo2 = new GeoPoint(lat2, lon2);
Glenn Van Schil
  • 1,059
  • 3
  • 15
  • 33

1 Answers1

11

Use argThat:

public final class IsSameLatLong extends ArgumentMatcher<GeoPoint> {

  private final GeoPoint as;

  public IsSameLatLong(GeoPoint as) {
      this.as = as;
  }

  //some sensible value, like 1000th of a second i.e. 0° 0' 0.001"
  private final static double EPSILON = 1.0/(60*60*1000); 

  private static boolean closeEnough(double a, double b) {
     return Math.abs(a - b) < EPSILON;
  }

  public boolean matches(Object point) {
      GeoPoint other = (GeoPoint) point;
      if (other == null) return false;
      return closeEnough(other.getLat(), as.getLat()) &&
             closeEnough(other.getLong(), as.getLong());
  }
}

Then use like this:

when(cityDefinitionRepository.findCitiesNearby(argThat(new IsSameLatLong(geoPoint)), eq(SOURCE), eq(2))).thenReturn(cityDefinitionsArrival);
when(cityDefinitionRepository.findCitiesNearby(argThat(new IsSameLatLong(geoPoint2)), eq(SOURCE), eq(2))).thenReturn(cityDefinitionsDeparture);
weston
  • 54,145
  • 21
  • 145
  • 203
  • Why `ArgumentMatcher` instead of `ArgumentMatcher`? – Robby Cornelissen Jan 04 '16 at 09:35
  • @RobbyCornelissen because I copied and pasted from [here](https://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Matchers.html)! thanks! – weston Jan 04 '16 at 09:36
  • Gotcha. Upvoted. I think the double comparison will not cause issues if both doubles are initialized using the same literal representation (e.g. `48.861710`) as this should also result in the same bit representation. – Robby Cornelissen Jan 04 '16 at 09:38
  • It almost works! When i use only one "when" statement it works fine but when i have them both i get a nullpointer on "return closeEnough(other.getLat(), as.getLat()) &&" – Glenn Van Schil Jan 04 '16 at 10:03
  • I've added a null check line – weston Jan 04 '16 at 10:14