如何验证 Mockito 与在 运行 测试用例以外的新线程上发生的视图的交互?

How to verify Mockito interaction with the view that happen on new thread other than the one which is running the test case?

当调用 setView 然后 Presenter 在新线程上通过网络获取一些数据时,我遇到了以下情况。给出这个原因导致测试失败——实际上,与这个模拟的交互为零。但如果交互得到验证,它应该会通过。

测试用例

@Test
public void checkUnoRate() {
    ratePresenter.setView(rateView,Constants.UNO);
    verify(rateView,times(1)).showRate(new Rate());
}

里面"ratePresenter.setView"

Call<UnoRate> call1 = ratesAPI.getUnoRate();
            call1.enqueue(new Callback<UnoRate>() {
                @Override
                public void onResponse(Call<UnoRate> call,Response<UnoRate> response) {
                    UnoRate unoRate = response.body();
                    Rate rate = new Rate();
                    rate.setBuyRate(unoRate.getBuy());
                    rate.setSellRate(unoRate.getSell());
                    rate.setFee(0);
                    rateView.showRate(rate);
                    }

               });

一个非常简单的解决方案是使用 Mockito 的 verification with timeout 功能。这将重复重试验证直到超时,寻找在某个时间点通过的条件。

@Test
public void checkUnoRate() {
    ratePresenter.setView(rateView,Constants.UNO);
    verify(rateView, timeout(100).times(1)).showRate(new Rate());
}

然而,文档对此提出警告:"This feature should be used rarely - figure out a better way of testing your multi-threaded system."这可能是因为您引入了一个新的方面——时间——作为您真正想要检查的事物的代理,即所有队列都已处理。您甚至可以想象一个足够繁忙的虚拟机,其中保守的超时可能会导致测试在自动化测试系统中出现问题,但在开发机器上运行良好。

如果可行,您可以将 ratesAPI 切换为使用同步执行程序,或者您可以向 API 访问器添加所需的方法以阻塞测试线程,直到所有调用都异步返回:

@Test
public void checkUnoRate() {
    ratePresenter.setView(rateView,Constants.UNO);
    ratesAPI.flush(); // Implement this to perform a Thread.join on the callback thread,
                      // or otherwise wait until all callbacks have been called.
    verify(rateView,times(1)).showRate(new Rate());
}

或者,要从测试中删除多线程和外部API交互,同步模拟回调:

@Mock RatesAPI ratesApiMock;
@Mock Call<UnoRate> unoRateCallMock;
@Captor Callback<UnoRate> unoRateCallbackCaptor;

@Test
public void checkUnoRate() {
    // Set up mock.
    when(ratesApiMock.getUnoRate()).thenReturn(unoRateCallMock);

    // Perform the action.
    ratePresenter.setView(rateView,Constants.UNO);

    // Verify nothing happens yet.
    verify(rateView, never()).showRate(any());

    // Capture and trigger the callback.
    verify(unoRateCallMock).enqueue(unoRateCallbackCaptor.capture());
    unoRateCallbackCaptor.getValue().onResponse(yourCall, yourResponse);

    // Verify the asynchronous action.
    verify(rateView,times(1)).showRate(new Rate());
}

附带说明一下,最终您可能想要验证与 new Rate() 不同的参数。不使用 Mockito 匹配器时,Mockito 通过 equals 方法进行比较。