gpt4 book ai didi

android - RxJava 和改造的单元测试

转载 作者:行者123 更新时间:2023-12-02 11:54:58 26 4
gpt4 key购买 nike

我有这个调用 Rest API 并将结果作为 Observable (Single) 返回的方法:

fun resetPassword(email: String): Single<ResetPassword> {
return Single.create { emitter ->

val subscription = mApiInterfacePanda.resetPassword(email)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({ resetPasswordResponse ->
when(resetPasswordResponse.code()) {
200 -> {
resetPasswordResponse?.body()?.let { resetPassword ->
emitter.onSuccess(resetPassword)
}
}
else -> emitter.onError(Exception("Server Error"))
}
}, { throwable ->
emitter.onError(throwable)
})

mCompositeDisposable.add(subscription)

}
}

单元测试:
@Test
fun resetPassword_200() {
val response = Response.success(200, sMockResetPasswordResponse)
Mockito.`when`(mApiInterfacePanda.resetPassword(Mockito.anyString()))
.thenReturn(Single.just(response))

mTokenRepository.resetPassword(MOCK_EMAIL)

val observer = mApiInterfacePanda.resetPassword(MOCK_EMAIL)
val testObserver = TestObserver.create<Response<ResetPassword>>()
observer.subscribe(testObserver)

testObserver.assertSubscribed()
testObserver.awaitCount(1)
testObserver.assertComplete()
testObserver.assertResult(response)
}

我的问题是只有这条线被覆盖,其他线不会运行,这对我的总测试覆盖率有很大影响:
return Single.create { emitter ->

最佳答案

如果我没记错的话,这里发生的不止一件事。让我们分部分来看。

首先,您的“内部”观察者:

mApiInterfacePanda.resetPassword(email)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({ resetPasswordResponse -> ... })

在 android 主线程上观察并在后台线程上执行。据我所知,在大多数情况下,测试线程将在您的 mApiInterfacePanda .resetPassword 之前结束。有机会完成并运行。您并没有真正发布测试设置,所以我不确定这是否是一个实际问题,但无论如何都值得一提。这是解决此问题的两种方法:

RxJavaPlugins 和 RxAndroidPlugins

RxJava 已经提供了一种更改提供的调度程序的方法。一个例子是 RxAndroidPlugins.setMainThreadSchedulerHandler .以下是它可以提供的帮助:
@Before
fun setUp() {
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
RxJavaPlugins.setInitIoSchedulerHandler { Schedulers.trampoline() }
}

上述方法确保在您使用主线程调度程序和 io 调度程序的任何地方,它都会返回 trampoline调度器。这是一个调度程序,可保证代码在之前执行的同一线程中执行。换句话说,它将确保您在单元测试主线程上运行它。

您将不得不撤消这些:
@After
fun tearDown() {
RxAndroidPlugins.reset()
RxJavaPlugins.reset()
}

您还可以更改其他调度程序。

注入(inject)调度器

您可以使用 kotlin 的默认参数来帮助注入(inject)调度程序:
fun resetPassword(
email: String,
obsScheduler: Scheduler = AndroidSchedulers.mainThread(),
subScheduler: Scheduler = Schedulers.io()
): Single<ResetPassword> {
return Single.create { emitter ->

val subscription = mApiInterfacePanda.resetPassword(email)
.observeOn(obsScheduler)
.subscribeOn(subScheduler)
.subscribe({ resetPasswordResponse ->
when(resetPasswordResponse.code()) {
200 -> {
resetPasswordResponse?.body()?.let { resetPassword ->
emitter.onSuccess(resetPassword)
}
}
else -> emitter.onError(Exception("Server Error"))
}
}, { throwable ->
emitter.onError(throwable)
})

mCompositeDisposable.add(subscription)
}
}

在测试时,您可以将其称为 resetPassword("foo@bar.com", Schedulers.trampoline(), Schedulers.trampoline()对于应用程序,只需通过电子邮件。

我在这里看到的另一件事可能与问题无关,但我认为仍然很高兴知道。首先,您正在创建一个单曲,但您不需要这样做。
Single.create通常在您没有响应式代码时使用。但是, mApiInterfacePanda.resetPassword(email)已经返回了一个 react 组件,虽然我不确定,但我们假设它是一个单一的。如果没有,将其转换为其他东西应该相当简单。

您还坚持使用一次性用品,据我所知,这不是必需的。

最后,您正在根据标签使用改造,因此除非非常必要,否则您不需要使调用返回原始响应。这是真的,因为改造会为您检查状态代码并将错误传递到 onError 中。有一个 http 异常(exception)。这是处理错误的 Rx 方式。

考虑到所有这些,我将像这样重写整个方法:
fun resetPassword(email: String) = mApiInterfacePanda.resetPassword(email)

(注意 resetPassword 不能返回原始响应,但 Single<ResetPassword>
它实际上不应该需要任何其他东西。改造将确保事情以 onSuccess 结束。或 onError .您无需在此处订阅 api 的结果并处理一次性用品 - 让调用此代码的人处理它。

您可能还注意到,如果是这种情况,则不需要调度程序的解决方案。我想在这种情况下确实如此,请记住某些运算符在某些默认调度程序中运行,并且在某些情况下您可能需要覆盖它们。

那么我将如何测试上述方法呢?

就我个人而言,我只是检查该方法是否使用正确的参数调用了 api:
@Test
fun resetPassword() {
mTokenRepository.resetPassword(MOCK_EMAIL)

verify(mApiInterfacePanda).resetPassword(MOCK_EMAIL)
}

我认为这里不需要更多。在重写的方法中我看不到更多的逻辑。

关于android - RxJava 和改造的单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61889243/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com