如何测试观察者?
问题描述:
我将考验主持人是这样的:如何测试观察者?
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
}
答
通常情况下,绝对不可能测试的情况不是很多。据我看到它,你有几种选择:
- 把观测到带有默认值的构造(但这也可能有一些缺点与依赖注入)
- 把观测到的功能具有默认值。这会起作用,但是你必须选择你的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>() {
我真的很喜欢“thenAnswer”机制。我写测试就像你说的,我很好。谢谢! – Hype
我不认为这是使用'thenAnswer'的有意的方式,或者它只是一个小小的破解。如果这有助于您,我会以另一种方式更新我的答案 – Pelocho