【问题标题】:Can't verify mock method call from RxJava Subscriber无法验证来自 RxJava 订阅者的模拟方法调用
【发布时间】:2016-03-01 12:58:51
【问题描述】:

我正在尝试在我的 Android 应用中对演示者进行单元测试。我尝试测试的方法如下所示:

@Override
public boolean loadNextPage() {
    if (!mIsLoading) {
        mIsLoading = true;
        if (mViewReference.get() != null) {
            mViewReference.get().showProgress();
        }
        mService.search(mSearchQuery, ++mCurrentPage)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(itemsPage -> {
                            mIsLoading = false;
                            mTotalPages = itemsPage.getPagination().getTotalPages();
                            if (mViewReference.get() != null) {
                                mViewReference.get().showMovies(itemsPage.getItems());
                            }
                        },
                        error -> {
                            mIsLoading = false;
                            Log.d(LOG_TAG, error.toString());
                        });
    }
    return mTotalPages == 0 || mCurrentPage < mTotalPages;
}

mService 是 Retrofit 接口,mService.search() 方法返回 RxJava 的 Observable&lt;SearchResults&gt;。我的单元测试代码如下所示:

package mobi.zona.presenters;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.ArrayList;
import java.util.List;

import com.example.api.Service;
import com.example.model.Movie;
import com.example.model.SearchResults;
import com.example.views.MoviesListView;
import rx.Observable;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class SearchPresenterTest {

    @Mock
    Service mService;
    @Mock
    MoviesListView mMoviesListView;

    @Test
    public void testLoadNextPage() throws Exception {
        String searchQuery = "the hunger games";
        SearchResults searchResults = new SearchResults();
        List<Movie> movies = new ArrayList<>();
        searchResults.setItems(movies);

        when(mService.search(searchQuery, 1)).thenReturn(Observable.just(new SearchResults()));

        MoviesListPresenter presenter = new SearchPresenter(mZonaService, mMoviesListView, searchQuery);
        presenter.loadNextPage();

        verify(mService, times(1)).search(searchQuery, 1);
        verify(mMoviesListView, times(1)).showProgress();
        verify(mMoviesListView, times(1)).showMovies(movies);
    }
}

问题是第三行verify(mMoviesListView, times(1)).showMovies(movies); - 它总是失败。当我试图调试这个测试时,我看到控制流永远不会进入.subscribe(itemPage - {...。我认为这与我订阅Schedulers.io() 线程的事实有关,但不知道如何解决这个问题。有任何想法吗? 编辑 1: 将演示者更改为将 Scheduler's 作为构造函数参数。将测试更改为如下所示:

@Test
public void testLoadNextPage() throws Exception {
    String searchQuery = "the hunger games";
    SearchResults searchResults = new SearchResults();
    List<Movie> movies = new ArrayList<>();
    searchResults.setItems(movies);

    when(mZonaService.search(searchQuery, 1)).thenReturn(Observable.just(new SearchResults()));

    MoviesListPresenter presenter = new SearchPresenter(mZonaService, mMoviesListView, searchQuery,
            Schedulers.test(), Schedulers.test());
    presenter.loadNextPage();

    verify(mZonaService, times(1)).search(searchQuery, 1);
    verify(mMoviesListView, times(1)).showProgress();
    verify(mMoviesListView, times(1)).showMovies(movies);
}

仍然收到此测试失败消息:

Wanted but not invoked:
mMoviesListView.showMovies([]);
-> at com.example.presenters.SearchPresenterTest.testLoadNextPage(SearchPresenterTest.java:46)

However, there were other interactions with this mock:
mMoviesListView.showProgress();
-> at com.example.presenters.SearchPresenter.loadNextPage(SearchPresenter.java:41)

【问题讨论】:

  • 不完全确定,但不应该像.showMovies(eq(movies));那样测试吗?

标签: java android unit-testing retrofit rx-java


【解决方案1】:

在我的应用程序中,interactors/use-cases/model(在您的情况下为 mService)负责为操作指定 Scheduler(因为它更清楚自己执行哪种操作)。

因此,请将您的 subscribeOn 移至 mService。之后,您的模拟将正常工作。

再深入一点,如果现在你想测试mService,我建议你让它“依赖”Scheduler。换句话说 - 添加Sheduler 作为构造函数参数。

public class MyService {

    private final Scheduler taskScheduler;

    public MyService(Scheduler taskScheduler) {
        this.taskScheduler = taskScheduler;
    }

    // ...

    public Observable<Something> query() {
        return someObservable.subscribeOn(taskScheduler);
    }
}

然后,在测试中你可以使用Schedulers.immediate() 和实际应用Schedulers.io()(或者你喜欢的任何东西,真的)。

【讨论】:

  • mService 实际上是 Retrofit 接口——它是我放置 @GET(/blah/blah) Observable&lt;SearchResult&gt; search(); 等方法定义的地方。所以我不能将订阅移入其中。
  • @FyodorSherstobitov 啊,我明白了。我总是在模型和演示者(交互者)之间增加一层。如果这不是您的选择,那么您可以改为使 Presenter 依赖于特定的 Scheduler(如我的示例中的 MyService,但将其应用于 Presenter
  • 按照您的建议更新了演示者。仍然测试失败。用新代码更新了主帖。
  • @FyodorSherstobitov Schedulers.test() 不是你需要的,试试Schedulers.immediate()
  • 更改为Schedulers.immediate() - 结果是一样的。我将尝试在交互器中隔离对 Retrofit 的调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多