我有这个结构的类,我需要测试OnRequestListOfLunchsFinished
接口
@Override
public void getListOfLunchs(final OnRequestListOfLunchsFinished callback) {
zip().onErrorResumeNext(new Function<Throwable, ObservableSource<? extends LunchServiceResponse>>() {
@Override
public ObservableSource<? extends LunchServiceResponse> apply(@NonNull Throwable throwable) throws Exception {
callback.onError(new RuntimeException(throwable));
callback.onEnd();
return Observable.empty();
}
}).subscribe(new Consumer<LunchServiceResponse>() {
@Override
public void accept(LunchServiceResponse response) throws Exception {
List<Lunch> result = new ArrayList<>();
List<IngredientResponseVO> ingredients = response.getIngredients();
Map<Integer, Ingredient> hash = new HashMap<Integer, Ingredient>();
for (IngredientResponseVO vo : ingredients)
hash.put(vo.id, new Ingredient(vo.id, vo.name, new BigDecimal(vo.price.toString()), vo.image));
for(InfoLunchResponseVO vo: response.getLunch()){
Lunch lunch = new Lunch();
lunch.setId(vo.id);
lunch.setImage(vo.image);
lunch.setName(vo.name);
for(Integer id : vo.ingredients){
Ingredient ingredient = hash.get(id);
lunch.addIngredient(ingredient);
}
result.add(lunch);
}
callback.onSuccess(result);
callback.onEnd();
}
});
callback.onStart();
}
private Observable<LunchServiceResponse> zip(){
return Observable.zip(getRequestOfListOfLunchs(), getRequestOfListOfIngredients(), new BiFunction<List<InfoLunchResponseVO>, List<IngredientResponseVO>, LunchServiceResponse>() {
@Override
public LunchServiceResponse apply(@NonNull List<InfoLunchResponseVO> infoLunchResponseVOs, @NonNull List<IngredientResponseVO> ingredientResponseVOs) throws Exception {
return new LunchServiceResponse(infoLunchResponseVOs, ingredientResponseVOs);
}
});
}
我有这种测试方法
@Test
public void teste(){
List<IngredientResponseVO> ingredients = Collections.emptyList();
List<InfoLunchResponseVO> lunchs = Collections.emptyList();
when(mockApi.getListOfIngredients()).thenReturn(Observable.just(ingredients));
when(mockApi.getLunchs()).thenReturn(Observable.just(lunchs));
mockImplementation.getListOfLunchs(callback);
InOrder order = inOrder(callback);
order.verify(callback).onStart();
order.verify(callback).onSuccess(anyList());
order.verify(callback).onEnd();
order.verifyNoMoreInteractions();
}
但我收到了例外:
org.mockito.exceptions.verification.VerificationInOrderFailure:
Verification in order failure
Wanted but not invoked:
callback.onSuccess(<any>);
如果我这样做:
callback.onStart();
callback.onSuccess(Collections.<Lunch>emptyList());
callback.onEnd();
InOrder order = inOrder(callback);
order.verify(callback).onStart();
order.verify(callback).onSuccess(anyList());
order.verify(callback).onEnd();
order.verifyNoMoreInteractions();
这有效。
如何仅验证我的模拟callback
的调用?
答案 0 :(得分:0)
您必须不使用InOrder
对象。
mockImplementation.getListOfLunchs(callback);
Mockito.verify(callback).onStart();
Mockito.verify(callback).onSuccess(anyList());
Mockito.verify(callback).onEnd();
Mockito.verifyNoMoreInteractions();
答案 1 :(得分:-1)
AFAICS问题不在于测试,而在于您对测试结果的阅读(跳跃:我相信它在您的代码中发现了一个错误)。
可能在实际代码中,您的getListOfIngredients
和getLunchs
会执行一些网络请求,即它们与getListOfLunchs
和(zip
内部的调用异步)。因此,实际代码onStart
会立即在调用者线程上调用,而onSucess
和onEnd
稍后会被调用。但是在测试中,您使用非常 同步 Observable.just
来模拟这些API调用,因此执行的顺序是不同的:首先调用onSuccess
,然后onEnd
,最后是onStart
(如果您将模拟的callback
替换为仅在每次调用中记录方法名称的自定义verifyNoMoreInteractions
,则可以轻松验证这一点。
您可能已经成功了,因为您使用onStart
时会收到有关onSucess
错误顺序的错误消息。不幸的是,这不是它的工作原理。由于您之前已指定订单验证,因此会先检查它们。在这些检查中,没有“不再”的限制。所以发生的事情大致如下:
InOrder
被调用。 onStart
检查会忽略它,因为还没有onEnd
InOrder
被调用。 onStart
检查会忽略它,因为还没有onStart
InOrder
被调用。这符合onSucess
期望的内容,现在等待onSuccess
。然而,这(第二个)getListOfIngredients
永远不会出现,这正是错误所说的。那该怎么办?首先,我想说恕我直言,这次失败的测试确实在您的代码中发现了一个非常真实的错误。假设在将来某个时候有人向您的API添加了一个缓存层,因此有时getLunchs
和OnRequestListOfLunchsFinished
会立即返回同步结果。在这种情况下,您的代码会破坏onStart
的合同,callback.onStart();
应首先调用zip
。所以正确的方法是修复你的代码。一个明显但可能错误的方法是移动线
callback
到方法的开头。 (为什么它可能是错的?你的onEnd
可以抛出异常吗?如果是,那么$a = [
[
"id" => "1",
"values" => [
"1",
"2"
]
],
[
"id" => "2",
"values" => [
"1",
"3"
]
],
[
"id" => "3",
"values" => [
"2",
"4"
]
],
[
"id" => "4",
"values" => [
"4",
"6"
]
],
];
的状态会发生什么?另一种方法是对$result = [
[
"id" => "1",
"values" => [
"1",
"2"
]
],
[
"id" => "3",
"values" => [
"2",
"4"
]
],
];
执行相同的操作,即以正确的顺序将其复制到成功和错误处理代码中。