如何测试观察者?

问题描述:

我将考验主持人是这样的:如何测试观察者?

class MostPopularPresenter @Inject constructor(val mostPopularUseCase: MostPopularUseCase) 
: Presenter<MostPopularView>() { 

    fun requestMostPopular(page: Int, update: Boolean) { 
     if (page <= 6) 
      mostPopularUseCase.execute(MostPopularObserver(), MostPopularUseCase.Params.createQuery(page, 15, update)) 
    } 

    inner class MostPopularObserver : DisposableSingleObserver<MostPopularModel>() { 

     override fun onSuccess(t: MostPopularModel) { 
      [email protected]?.populateRecyclerList(t) 
     } 

     override fun onError(e: Throwable) { 
      [email protected]?.showError() 
     } 
    } 
} 

我有问题怎么嘲笑观察者,迫使它抛出错误或成功返回值。我正在使用mockito/junit。有人能指出我如何实现它吗?也许我的代码是不可测试的?

一个observer是不应该真正测试的对象。它已经由第三方开发者进行了测试,虽然有些人认为,由于部分原因,你还应该测试第三方库以确保它不会破坏你的代码版本之间。

所以,如果你不测试observer ......你如何测试你的代码?简单地说,你真正需要测试的是发言人本身。运行在observer内的代码是主讲者的一部分。因此,而不是嘲讽observer模拟的useCase

test useCaseFails() { 
    val usecase = // mock use case 
    when(usecase.execute(...)) 
     .thenAnswer(/* receive the observer as first parameter 
         and make it emit an error */) 
    val presenter = ... 
    presenter.requestMostPopular(...) 
    // assert that presenter.view?.showError has been called 
} 

这样做的另一种方式(至少这是我的方式通常是代码)是使useCase返回observable并在presenter订阅吧:

class MostPopularPresenter @Inject constructor(val mostPopularUseCase: MostPopularUseCase) 
    : Presenter<MostPopularView>() { 

    private var lateinit observer : Disposable 

    fun requestMostPopular(page: Int, update: Boolean) { 
     if (page <= 6) 
      disposable = mostPopularUseCase.execute(MostPopularUseCase.Params.createQuery(page, 15, update)) 
       .subscribe(t -> view?.populateRecyclerList(t), 
          e -> view?.showError()) 
    } 
} 

这样你就可以很容易地嘲笑你useCase所以它返回一个Subject你可以控制:

test useCaseFails() { 
    val usecase = // mock use case 
    val subject = PublishSubject() 
    when(usecase.execute(...)) 
     .thenReturn(subject) 
    val presenter = ... 
    presenter.requestMostPopular(...) 
    subject.emitError(...) // <- pseudocode 
    // assert that presenter.view?.showError has been called 
} 
+0

我真的很喜欢“thenAnswer”机制。我写测试就像你说的,我很好。谢谢! – Hype

+0

我不认为这是使用'thenAnswer'的有意的方式,或者它只是一个小小的破解。如果这有助于您,我会以另一种方式更新我的答案 – Pelocho

通常情况下,绝对不可能测试的情况不是很多。据我看到它,你有几种选择:

  • 把观测到带有默认值的构造(但这也可能有一些缺点与依赖注入)
  • 把观测到的功能具有默认值。这会起作用,但是你必须选择你的API是否应该包含这个
  • 使用observer作为属性。在测试中,你可以覆盖这个。

所有这些变种会的工作,在这里列出:

// observer in constructor 
class MostPopularPresenter @Inject constructor(val mostPopularUseCase: MostPopularUseCase, val observer: DisposableSingleObserver<MostPopularModel> = MostPopularObserver()) 
    : Presenter<MostPopularView>() { 

    // observer as property 
    internal var observer: DisposableSingleObserver<MostPopularModel> = MostPopularObserver() 

    // observer in function 
    fun requestMostPopular(page: Int, update: Boolean, observer: DisposableSingleObserver<MostPopularModel> = MostPopularObserver()) { 
     if (page <= 6) 
      mostPopularUseCase.execute(observer, MostPopularUseCase.Params.createQuery(page, 15, update)) 
    } 
} 

internal class MostPopularObserver : DisposableSingleObserver<MostPopularModel>() { ... } 

它甚至会更好,如果我们DisposableSingleObserverFactory并在需要时创建观察者。

class MostPopularPresenter @Inject constructor(val mostPopularUseCase: MostPopularUseCase, val observerFactory: DisposableSingleObserverFactory<MostPopularModel> = MostPopularObserverFactorty()) 
    : Presenter<MostPopularView>() { 

    internal var observerFactory: DisposableSingleObserverFactory<MostPopularModel> = MostPopularObserverFactory() 

    fun requestMostPopular(page: Int, update: Boolean, observerFactory: DisposableSingleObserverFactory<MostPopularModel> = MostPopularObserver()) { 
     if (page <= 6) 
      mostPopularUseCase.execute(observerFactory.create(), MostPopularUseCase.Params.createQuery(page, 15, update)) 
    } 
} 

internal class MostPopularObserver : DisposableSingleObserver<MostPopularModel>() {