android 在 MVP Presenter 中对 RxJava 进行单元测试

Unit testing RxJava in MVP presenter in android

我是 TDD 新手。也是 MVP 和 Rxjava 的新手。我只是潜入其中,这是值得的。但我停留在测试部分。我了解单元测试的基础。一开始对我来说有点困难。但是我卡在这里了那么如何测试演示者呢?

这里是演示者class -

public class NewsPresenter {

private final RxjavaService service;
private final MainView view;
private CompositeSubscription subscriptions;

public NewsPresenter(RxjavaService service, MainView view) {
    this.service = service;
    this.view = view;
    subscriptions = new CompositeSubscription();
}

public void getNewsList(String urlQ){
    view.showWait();

    Subscription subscription = service.getNews(urlQ ,new RxjavaService.GetNewsCallback() {
        @Override
        public void onSuccess(Articles articles) {
            view.removeWait();
            view.getNewsListSuccess(articles);
        }

        @Override
        public void onError(NetworkError networkError) {
            view.removeWait();
            view.onFailure(networkError.getAppErrorMessage());
            Log.i("huh",networkError.getMessage());
        }
    });

    subscriptions.add(subscription);
}

public void onStop(){
    subscriptions.unsubscribe();
}

}

这是查看界面 -

public interface MainView {

void showWait();

void removeWait();

void onFailure(String appErrorMessage);

void getNewsListSuccess(Articles articles);

}

这里是 RxJavaService class -

public class RxjavaService {

private final NewsRestService newsRestService;

public RxjavaService(NewsRestService newsRestService) {
    this.newsRestService = newsRestService;
}


public interface GetNewsCallback {
    void onSuccess(Articles articles);

    void onError(NetworkError networkError);
}


public Subscription getNews(String q, final GetNewsCallback getNewsCallback) {
    Log.i("stuck","service called");
    return newsRestService.getNewsBySearch(q,"8dca7dea475e41e49518b2c61131e118",100)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .onErrorResumeNext(new Func1<Throwable, Observable<? extends Articles>>() {
                @Override
                public Observable<? extends Articles> call(Throwable throwable) {
                    return Observable.error(throwable);
                }
            })
            .subscribe(new Subscriber<Articles>() {
                @Override
                public void onCompleted() {
                    Log.i("stuck","complete");
                }

                @Override
                public void onError(Throwable e) {
                    getNewsCallback.onError(new NetworkError(e));
                    Log.i("stuck",e.getMessage());
                }

                @Override
                public void onNext(Articles articles) {
                    getNewsCallback.onSuccess(articles);
                    Log.i("stuck","Onnext");
                }
            });
}

}

这是我卡住的测试 class-

@RunWith(MockitoJUnitRunner.class)

public class NewsListTest {

private NewsPresenter newsPresenter;

@Mock
private RxjavaService rxjavaService;
@Mock
private MainView mainView;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);

    newsPresenter = new NewsPresenter(rxjavaService,mainView);
}

@After
public void tearDown() throws Exception {
    mainView = null;
    newsPresenter.onStop();
}

@Test
public void Testing_The_Result() {

}

}

要事第一

  1. 如果您喜欢 TDD,就永远不会遇到您描述的情况。在 TDD 中,你 从失败的测试开始 ,然后就去编写实现。所以你的问题更多是关于测试而不是 TDD。
  2. 我建议切换到 RxJava2,因为 RxJava1 在 3 月 31 日达到生命周期结束。
  3. 我觉得很奇怪 RxJavaService 将 API 从 publish/subscribe 更改为回调。为什么不坚持使用 rx API 一直到演示者?

使用模拟的 RxJavaService 进行测试

如果您想使用测试中的设置完成测试编写,它看起来像这样:

@Test
public void Testing_The_Result() {
    final RxjavaService.GetNewsCallback[] callback = new RxjavaService.GetNewsCallback[1];
    Mockito.when(rxjavaService.getNews(ArgumentMatchers.anyString(), ArgumentMatchers.any(RxjavaService.GetNewsCallback.class))).thenAnswer(new Answer<Subscription>() {
        public Subscription answer(InvocationOnMock invocationOnMock) {
            callback[0] = invocationOnMock.getArgument(1);
            return mock(Subscription.class);
        }
    });
    newsPresenter.getNewsList("some url");
    Articles articles = new Articles();

    callback[0].onSuccess(articles);

    verify(mainView).removeWait();
    verify(mainView).getNewsListSuccess(articles);
}

您可以通过不使用 Mockito 模拟 RxJavaService 来摆脱丑陋的代码,而是使用您自己的 hand-written 模拟,它将存储回调并将其提供给测试。

不过,我建议采用不同的方法。

使用真实的 RxJavaService 和模拟的 NewsRestService 进行测试

我想说如果我们只模拟 NewsRestService:

会更有意义并提供更好的测试
@RunWith(MockitoJUnitRunner.class)
public class NewsList2Test {

    private NewsPresenter newsPresenter;

    @Mock
    private MainView mainView;
    @Mock
    private NewsRestService newsRestService;

    @Before
    public void setUp() {
        newsPresenter = new NewsPresenter(new RxjavaService(newsRestService), mainView);
    }

    @Test
    public void show_success_in_view_when_there_are_articles() {
        when(newsRestService.getNewsBySearch(eq("some url"), anyString(), anyInt()))
                .thenReturn(Observable.just(new Articles()));

        newsPresenter.getNewsList("some url");

        verify(mainView).removeWait();
        verify(mainView).getNewsListSuccess(any(Articles.class));
    }

}