【问题标题】:How to test observer?如何测试观察者?
【发布时间】:2017-07-11 22:43:24
【问题描述】:

我会像这样测试演示者:

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。有人可以指出我如何实现它吗?也许我的代码无法测试?

【问题讨论】:

    标签: unit-testing mockito kotlin rx-java


    【解决方案1】:

    observer 是一个不应该被真正测试的对象。它在由第三方开发人员开发时已经过测试,尽管有些人说,有部分原因,您还应该测试第三方库以确保它不会破坏您的代码版本之间。

    那么,如果你不测试observer...你如何测试你的代码?简单地说,您真正需要测试的是 presenter 本身。在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
    }
    

    【讨论】:

    • 我真的很喜欢“thenAnswer”机制。我像你说的那样写了测试,而且效果很好。谢谢!
    • 我不认为这是使用thenAnswer 的预期方式,或者它只是一个小技巧。如果这对你有帮助,我会用另一种方式更新我的答案
    【解决方案2】:

    通常情况下,绝对无法测试的情况并不多。在我看来,你有几个选择:

    • 使用默认值将观察者放入构造函数(但这可能对依赖注入有一些不利影响)
    • 使用默认值将观察者放入函数中。这可行,但您必须选择您的 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>() {
    

    【讨论】:

      猜你喜欢
      • 2018-03-02
      • 2021-06-24
      • 2011-06-10
      • 2020-07-25
      • 1970-01-01
      • 2020-06-01
      • 2021-08-12
      • 2019-04-09
      • 1970-01-01
      相关资源
      最近更新 更多