【问题标题】:Understanding how to test RxJava Observables了解如何测试 RxJava Observables
【发布时间】:2017-04-09 05:01:58
【问题描述】:

我正在学习如何编写 android 单元测试。而且,我正在查看示例:所以,我看到了这样的内容:

@Test
public void getPopularMoviesMakesApiCall() {
    // given that the api service returns a response
    1. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));

    // when getPopularMovies is invoked
    2. mRemoteRepository.getPopularMovies(1).subscribeWith(mMovieListTestSubscriber);

    // then, verify that the api request is made and returns the expected response
    3. verify(apiService).discover(SORT_BY_POPULARITY, PAGE, API_KEY);
    4. mMovieListTestSubscriber.assertValue(mMovieList);
}

我尝试运行它,我注意到选项 1 总是执行,选项 2 也执行。但是,如果选项 3 与选项 2 中的信息不符, 它会抛出一个错误,说它们不一样。这意味着选项 3 确认选项 2。如果我错了或有任何需要纠正的地方,请 一定要告诉。所以,我写了这样的东西:

@Test
public void testBadHashException() throws Exception {

  1. mRemoteRepository.getPopularMovies(1, FAKE_API_KEY).subscribeWith(mMovieListTestSubscriber);

  2. mMovieListTestSubscriber.assertNoValues();
  3. mMovieListTestSubscriber.assertError(HttpException.class);
}

这是我注意到的:

private List<Movie> mMovieList;
private DiscoverMoviesResponse mDiscoverMoviesResponse;

private MoviesRepository mRemoteRepository;

private TestObserver<List<Movie>> mMovieListTestSubscriber;
private TestObserver<Movie> mMovieTestSubscriber;

@Mock
MovieApiService apiService;

上面的那些,在顶部声明,并由Mockito @Before @annotation 初始化,如下所示:

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);

    mRemoteRepository = new MoviesRemoteRepository(apiService);

    mMovieTestSubscriber = new TestObserver<>();
    mMovieListTestSubscriber = new TestObserver<>();

    mMovieList = TestDataGenerator.generateMovieList(10);
    mDiscoverMoviesResponse = TestDataGenerator.generateDiscoverMoviesResponse(mMovieList);
}

注意:TestDataGenerator 是用于生成数据的辅助类。在那里完成后,他得到了 MovieList,然后得到了另一个,它是主要的响应体。

APIService:改造服务类。 MoviesRepository:用于在服务类中操作 Observable 的辅助类。由 ViewModel 使用。

第二个测试一直给我java.lang.RuntimeException: No mock defined for invocation。我似乎还没有明白。 是否有一个特定的实例,我应该在何时使用、验证、如何测试可观察到的改造请求错误。

如果它说没有 Mock 数据,但完成后已经生成了 Mock 数据。还是应该以不同的方式嘲笑它?

mMovieList = TestDataGenerator.generateMovieList(10); mDiscoverMoviesResponse = TestDataGenerator.generateDiscoverMoviesResponse(mMovieList);

更多关于我的观察:

我正在通过Mockito,我注意到,第一个通过的测试正在执行,因为他这样做了:

1. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));

由于第二个函数的错误显示java.lang.RuntimeException: No mock defined for invocation,说明该方法 在一个类中可以使用when("some method").thenReturn() 进行模拟,这没关系。然后我修改了我的testBadHashException 看起来像这样:

@Test
public void testBadHashException() throws Exception {

  0. when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)).thenReturn(Observable.just(mDiscoverMoviesResponse));

  1. mRemoteRepository.getPopularMovies(1, FAKE_API_KEY).subscribeWith(mMovieListTestSubscriber);

  2. mMovieListTestSubscriber.assertNoValues();
  3. mMovieListTestSubscriber.assertError(HttpException.class);
}

它没有抛出异常,而是抛出了成功。

我重写了错误测试:

@Test
public void getPopularMoviesThrowsError() {
    when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(Observable.<List<Movie>>error(new TimeoutException()));

    // request movies
    mMoviesViewModel.discoverMovies(true);

    verify(mMoviesRepository).getPopularMovies(PAGE);

    // check that empty view is hidden
    assertFalse(mMoviesViewModel.emptyViewShowing.get());

    // check that loading view is hidden
    assertFalse(mMoviesViewModel.moviesLoading.get());

    // check that error view is showing
    assertTrue(mMoviesViewModel.errorViewShowing.get());
}

这里有编译错误:when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(Observable.&lt;List&lt;Movie&gt;&gt;error(new TimeoutException()));

这里无法解析方法:Observable.&lt;List&lt;Movie&gt;&gt;error(new TimeoutException())

与 JavaScript 相比,在 Android 中编写测试看起来很奇怪。任何有关我如何学习或理解如何编写单元测试的帮助将不胜感激。我刚收养 MVVM 模式,我正在尝试用它编写测试。谢谢。

【问题讨论】:

  • 当你调用 getPopularMovies(1, FAKE_API_KEY) 你的 apiService 的哪个方法会被调用?你有没有用 mockito 模拟过这个方法??
  • @LeandroBorgesFerreira 我更新了我的问题
  • 什么是投掷成功?测试还是 getPopularMovies Observable?我不确定您要做什么...您是否需要 apiService 返回一个引发异常的 Observable?你需要改变 Observable 来抛出你需要的异常
  • @LeandroBorgesFerreira 当它说 Mock 为空时,我有点理解。接口服务类中的函数为空。这就是when(apiService.discover(SORT_BY_POPULARITY, PAGE, API_KEY)) 的精髓。我想测试错误,所以我做了这样的事情:(请检查更新的问题)
  • @LeandroBorgesFerreira 如果我更改 Observable 以引发错误,你是说我应该更改 Object APIService Observable 函数正在发射吗?

标签: android retrofit2 rx-java2


【解决方案1】:

如果你需要向你的 Observable 发送一个错误,你可以这样创建它:

when(mMoviesRepository.getPopularMovies(PAGE)).thenReturn(  
    Observable.create(new Observable.OnSubscribe<SomeClass>() { 
        @Override public void call(Subscriber<SomeCladd> subscriber) {
          subscriber.onError(new Exception("some message!")); 
         } 
        }));

这样你就会有一个返回错误的 Observable 以便你可以调用

mMovieListTestSubscriber.assertError

您遇到的问题是您使用 Observable.just 来创建您的 Observable...所以永远不会调用 onError 方法,只会调用 onNext。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-20
    • 2019-08-13
    • 1970-01-01
    • 2017-09-07
    • 2018-06-20
    • 1970-01-01
    • 2014-12-02
    相关资源
    最近更新 更多