【问题标题】:Mock a method with an object parameter with Mockito使用 Mockito 模拟带有对象参数的方法
【发布时间】:2016-04-07 20:45:02
【问题描述】:

在我的单元测试中,我想通过执行以下操作来模拟与 elasticsearch 的交互

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

doSearch 方法包含

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

当我调试我的代码时,我看到在我的 doSearch 方法中调用了 mockito,但它没有返回 cityDefinitionsArrival 对象。这可能是因为 geoPoint 和 geo 是两个不同的对象。

geoPoint和geo对象都是elasticsearch GeoPoints,包含相同的经纬度。

我设法让它工作了

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

但现在它忽略了我的纬度和经度值并接受 GeoPoint 类的任何对象。这是一个问题,因为在我的 doSearch 方法中,我有两个 findCitiesNearby 的用法,每个都有不同的纬度和经度,我需要分别模拟它们。

Mockito 可以做到这一点吗?

cityDefinitionsArrival 和 cityDefinitionsDeparture 都是 ArrayLists, SOURCE 是一个字符串值,并且 geo 和 geoPoint 对象:

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);

【问题讨论】:

  • 大家都清楚,当GeoPoint 对象具有一定的纬度/经度坐标时,您想模拟行为,对吗?
  • 如果GeoPoint 类定义了equals 方法,我想你应该可以使用eq() 匹配器。如果没有,请实现自定义 Mockito ArgumentMatcher
  • @RobbyCornelissen 那么它默认做什么呢? == 比较?
  • @TimBiegeleisen 正确,返回 cityDefinitionsArrival 或 cityDefinitionsDeparture 取决于使用的纬度和经度。如果是地理,则返回 cityDefinitionsArrival。如果是 geo2,则返回 cityDefinitionsDeparture。
  • @GlennVanSchil 我后来添加了它:) 应该像实现class GeoPointMatcher extends ArgumentMatcher<GeoPoint> 及其matches() 方法一样简单。

标签: java unit-testing mockito matcher stubbing


【解决方案1】:

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());
  }
}

然后像这样使用:

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);

【讨论】:

  • 为什么是ArgumentMatcher&lt;List&gt; 而不是ArgumentMatcher&lt;GeoPoint&gt;
  • @RobbyCornelissen 因为我是从here 复制和粘贴的!谢谢!
  • 明白了。赞成。我认为如果两个双精度都使用相同的文字表示(例如48.861710)初始化,则双精度比较不会导致问题,因为这也应该导致相同的位表示。
  • 它几乎可以工作了!当我只使用一个“when”语句时,它可以正常工作,但是当我同时拥有它们时,我会在“return closeEnough(other.getLat(), as.getLat()) &&”上得到一个空指针
  • 我添加了一个空检查行
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 2018-02-21
  • 1970-01-01
  • 1970-01-01
  • 2015-12-13
相关资源
最近更新 更多