首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何验证JUnit中模拟方法的顺序

如何验证JUnit中模拟方法的顺序
EN

Stack Overflow用户
提问于 2017-08-18 09:34:18
回答 2查看 555关注 0票数 1

我有一个具有这些结构的类,我需要测试OnRequestListOfLunchsFinished接口的行为

代码语言:javascript
运行
复制
@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);
        }

    });
}

我有这个测试方法

代码语言:javascript
运行
复制
@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();
}

但我收到了一个异常:

代码语言:javascript
运行
复制
org.mockito.exceptions.verification.VerificationInOrderFailure: 
Verification in order failure
Wanted but not invoked:
callback.onSuccess(<any>);

如果我这样做:

代码语言:javascript
运行
复制
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的调用

EN

回答 2

Stack Overflow用户

发布于 2017-08-18 13:52:09

您只需使用InOrder对象,而不是

代码语言:javascript
运行
复制
mockImplementation.getListOfLunchs(callback);

Mockito.verify(callback).onStart();
Mockito.verify(callback).onSuccess(anyList());
Mockito.verify(callback).onEnd();

Mockito.verifyNoMoreInteractions();
票数 0
EN

Stack Overflow用户

发布于 2017-08-20 09:14:06

AFAICS问题不在于测试,而在于你对测试结果的阅读(跳过:我相信它在你的代码中发现了一个bug )。

在实际代码中,您的getListOfIngredientsgetLunchs可能会执行一些网络请求,即它们与对getListOfLunchs和(其内部的zip)的调用是异步的。因此,在实际代码中,在调用者线程上立即调用onStart,而稍后调用onSucessonEnd。然而,在您的测试中,您使用synchronous Observable.just来模拟这些API调用,因此执行顺序是不同的:首先调用onSuccess,然后调用onEnd,最后调用onStart (如果您将模拟的callback替换为一个自定义的API,它只在每次调用中记录方法名称,则可以很容易地验证这一点)。

你可能已经预料到,由于你使用了verifyNoMoreInteractions,你会得到一个关于错误的onStart顺序的错误。不幸的是,这不是它的工作方式。由于您的订单验证是在较早的时间指定的,因此会在较早的时间进行检查。在这些检查中,还没有“不再”的限制。因此,发生的事情大致如下:

调用

  1. onSucess

那该怎么办呢?首先,我想说的是,这个失败的测试确实在你的代码中发现了一个非常真实的bug。假设在将来的某个时刻,有人向你的应用编程接口添加了一个缓存层,所以有时getListOfIngredientsgetLunchs会立即返回一个同步的结果。在这种情况下,您的代码违反了应该首先调用onStartOnRequestListOfLunchsFinished的约定。所以正确的方法是修复你的代码。一种明显但可能的错误方法是移动线条

代码语言:javascript
运行
复制
callback.onStart();

添加到方法的开头。(为什么它可能是错误的?你的zip能抛出异常吗?另一种方法是使用onEnd做同样的事情,即按正确的顺序将其复制到成功和错误处理代码中。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45747197

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档