Android 如何验证JUnit中模拟方法的顺序
我有一个具有这些结构的类,我需要测试Android 如何验证JUnit中模拟方法的顺序,android,junit,mockito,Android,Junit,Mockito,我有一个具有这些结构的类,我需要测试onrequestlistofluncsfinished接口的行为 @Override public void getListOfLunchs(final OnRequestListOfLunchsFinished callback) { zip().onErrorResumeNext(new Function<Throwable, ObservableSource<? extends LunchServiceResponse>&g
onrequestlistofluncsfinished
接口的行为
@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);
}
});
}
@覆盖
public void getListOfLunchs(最终OnRequestListOfLunchsFinished回调){
zip().onErrorResumeNext(新函数您只需不使用顺序对象即可
mockImplementation.getListOfLunchs(callback);
Mockito.verify(callback).onStart();
Mockito.verify(callback).onSuccess(anyList());
Mockito.verify(callback).onEnd();
Mockito.verifyNoMoreInteractions();
AFAICS问题不在于测试,而在于您对测试结果的阅读(向前跳:我相信它在您的代码中发现了一个bug)
可能在真实代码中,您的getListOfIngredients
和getLiunts
执行一些网络请求,即它们与调用getListOfLunchs
和(zip
内部)是异步的因此,在真正的代码中,onStart
会在调用线程上立即调用,而onsuces
和onEnd
会在稍后调用。但是,在测试中,您模拟这些API调用时,可以观察到非常同步的。只要
,执行顺序就不同了:首先onSuccess
调用,然后是onEnd
,最后是onStart
(如果您将模拟的回调
替换为只在每次调用中记录方法名称的自定义回调,则可以轻松验证这一点)
您可能已经体验到,由于您使用了verifyNoMoreInteractions
,您可能会得到一个关于onStart
的错误顺序的错误。不幸的是,这不是它的工作方式。由于您的订单验证是在更早的时候指定的,所以它们会在更早的时候被检查。在这些检查中,还没有“不再”的限制.因此,发生的情况大致如下:
调用了onsuces
。inoder
check忽略它,因为还没有onStart
onEnd
被调用。inoder
check忽略它,因为还没有启动onStart
调用了onStart
。这与InOrder
期望的匹配,现在它等待onSucess
。但是这(第二次)onSuccess
从未出现,这正是错误所说的
那么该怎么办呢?首先我想说的是,这次失败的测试确实在你的代码中发现了一个非常真实的错误。假设在将来的某个时候,有人在你的API中添加了一个缓存层,所以有时候getListofIngreents
和getLiunts
会立即返回同步结果。在这种情况下,你的代码会中断在onrequestlistofluncsfinished
中,应该首先调用onStart
。因此正确的方法是修复代码。一个明显但可能错误的方法是移动行
callback.onStart();
方法的开头。(为什么可能出错?您的zip
能否引发异常?如果引发异常,则回调的状态会发生什么变化?)。另一种方法是与使用ONED时相同,即按正确顺序将其复制到成功和错误处理代码中。但在这种情况下,调用顺序很重要:/
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();
mockImplementation.getListOfLunchs(callback);
Mockito.verify(callback).onStart();
Mockito.verify(callback).onSuccess(anyList());
Mockito.verify(callback).onEnd();
Mockito.verifyNoMoreInteractions();
callback.onStart();