如何测试观察者?

时间:2017-07-11 22:43:25

标签: unit-testing mockito kotlin rx-java

我会像这样测试主持人:

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) {
            this@MostPopularPresenter.view?.populateRecyclerList(t)
        }

        override fun onError(e: Throwable) {
            this@MostPopularPresenter.view?.showError()
        }
    }
}

我有问题如何模拟观察者并强制它在成功时抛出错误或返回值。我正在使用mockito / junit。有人能指出我如何实现它吗?也许我的代码是不可测试的?

2 个答案:

答案 0 :(得分:1)

通常情况下,绝对不可能进行测试的情况很少。据我所知,你有几个选择:

  • 将观察者放入具有默认值的构造函数中(但这可能会导致依赖注入的一些缺点)
  • 将观察者放入具有默认值的函数中。这可行,但您必须选择您的API是否应包含此
  • 使用观察者作为属性。在测试中,你可以覆盖这个。

所有这些变体都可以使用并列在这里:

// 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>() {

答案 1 :(得分:1)

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
}