我有一个使用UseCases访问存储库的应用程序。 UseCases在线程池上订阅,存储库使用OkHttp的线程池发出网络请求,并且存储库还写入使用Schedulers.io()线程池的数据库。
对于某些版本的Android,我无法使用Espresso进行UI测试,因为在所有网络请求/数据库写操作完成之前,我的空闲资源似乎都处于空闲状态。
AFAIK,我的设置使用了三个不同的线程池。我的想法是,如果在我的UI测试中创建了一个空闲资源来监视每个线程池,那么当我通过UseCase调用存储库时,Espresso将等到所有线程池都处于空闲状态后再进行操作测试代码。
这是我的测试代码的示例:
val wrapped: IdlingResourceScheduler = Rx2Idler.wrap(application.component.providesIoScheduler(), "IO Scheduler")
val otherWrapped: IdlingResourceScheduler = Rx2Idler.wrap(application.component.providesJobScheduler(), "J O B")
val resource = arrayOf<IdlingResource>(okHttpIdlingResource, wrapped, otherWrapped)
idlingRegistry.goIdle(*resource) {
onView(withId(R.id.toolbar_main))
.check(matches(isDisplayed()))
}
我想我在上面的代码中所做的是为数据库线程池,OkHttp线程池和UseCase线程池注册一个空闲资源。因此,我的想法是,当对我的存储库进行调用时,这些线程池中的至少一个将始终处于“活动”状态,并且一旦存储库返回其值,所有线程池都将处于空闲状态,而Espresso会知道它是可以进行测试。就像我在上面说的那样,这对于运行棉花糖(甚至更高版本)的设备似乎是正确的,但不适用于Pie上的设备。
这是UseCase的简化示例:
repository
.authenticate(params.username, params.password)
.subscribeOn(My Use Case Thread Pool)
.observeOn(Main Thread)
这是我的Repository类中auth方法的简化示例:
Single.zip(networkLayer.authenticate(username, password),
userDao.getUserInfo(),
BiFunction { t1, t2 -> Result(t1,t2) }
.flatMap {
Single.just(deleteAllDataFromDatabase())
.flatMap {
networkLayer.getRestOfData()
}
}
我真正不明白的是,为什么测试在某些版本上而不是其他版本上运行。我也无法理解为什么,如果我观察所有线程池的空闲情况,那么Espresso如何推进测试?至少我的一个线程池不会做某事(从网络中获取,写入数据库或在用例中做某事?我缺少什么?
答案 0 :(得分:0)
您需要将所有异步逻辑移到同一线程中。您可以使用针对RxJava / RxAndroid的测试规则来做到这一点
class ImmediateSchedulersRule : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() }
RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
try {
base.evaluate()
} finally {
RxJavaPlugins.reset()
RxAndroidPlugins.reset()
}
}
}
}
}
并将此规则添加到测试类中:
@get:Rule
var immediateRule = ImmediateSchedulersRule()
顺便说一句,如果您可以重构用例,则更好的解决方案是将Scheduler
对象传递给每个用例。
类似的东西:
将调度程序保存在自己的文件中并创建一个默认文件:
interface AppScheduler {
fun io(): Scheduler
fun computation(): Scheduler
fun main(): Scheduler
}
class DefaultAppScheduler : AppScheduler {
override fun io() = Schedulers.io()
override fun computation() = Schedulers.computation()
override fun main() = AndroidSchedulers.mainThread()
}
您的用例已经注入了AppScheduler(或者,如果您不使用Dagger或类似工具,则手动传递),并且在常规应用中,您将通过DefaultAppScheduler
实现。
class TestUseCase @Inject constructor(
private val scheduler: AppScheduler,
private val yourRepository: YourRepository
) : BaseUseCase<Unit, List<Object>>() {
//I have omitted some details here but you can get the point
fun createObservable(params: Unit): Flowable<List<Object>> {
return yourRepository.loadDataList()
.subscribeOn(scheduler.io())
}
}
然后,在测试代码中,您将创建一个新的AppScheduler
实现,仅用于使用trampoline
一个进行测试:
class TestAppScheduler : AppScheduler {
override fun io() = Schedulers.trampoline()
override fun computation() = Schedulers.trampoline()
override fun main() = Schedulers.trampoline()
}
通过这种方式,您的测试将等待进行。