使用 Mockk 测试 RxJava repeatWhen 返回许多
Testing RxJava repeatWhen with Mockk returnsMany
我正在尝试使用 Mockk 库测试多个服务器响应。类似于我在 this answer 中为 Mockito 找到的内容。
有我的示例 UseCase 代码,它每隔几秒就会重复调用以从远程服务器加载系统,当远程系统包含的用户多于本地时,它会停止 运行(onComplete
是已执行)。
override fun execute(localSystem: System, delay: Long): Completable {
return cloudRepository.getSystem(localSystem.id)
.repeatWhen { repeatHandler -> // Repeat every [delay] seconds
repeatHandler.delay(params.delay, TimeUnit.SECONDS)
}
.takeUntil { // Repeat until remote count of users is greater than local count
return@takeUntil it.users.count() > localSystem.users.count()
}
.ignoreElements() // Ignore onNext() calls and wait for onComplete()/onError() call
}
为了测试此行为,我正在使用 Mockk 库模拟 cloudRepository.getSystem()
方法:
@Test
fun testListeningEnds() {
every { getSystem(TEST_SYSTEM_ID) } returnsMany listOf(
Single.just(testSystemGetResponse), // return the same amount of users as local system has
Single.just(testSystemGetResponse), // return the same amount of users as local system has
Single.just( // return the greater amount of users as local system has
testSystemGetResponse.copy(
owners = listOf(
TEST_USER,
TEST_USER.copy(id = UUID.randomUUID().toString())
)
)
)
)
useCase.execute(
localSystem = TEST_SYSTEM,
delay = 3L
)
.test()
.await()
.assertComplete()
}
如您所见,我使用的是 returnsMany
答案,每次调用时应 return 不同的值。
主要问题是 returnsMany
return 每次都是相同的第一个值,并且 .takeUntil {}
永远不会成功,这意味着永远不会为此 Completable 调用 onComplete()
。如何使 returnsMany
return 在每次调用时具有不同的值?
您可能不了解 .repeatWhen()
的工作原理。您希望每次请求重复时都会调用 cloudRepository.getSystem(id)
。那是不正确的。重复订阅一直在模拟 Single
的同一个实例上完成 - 在您的情况下是第一个 Single.just(testSystemGetResponse)
。
如何确保每次都调用getSystem()
?将您的 Single 包装成 Single.defer()
。它类似于 Single.fromCallable()
但传递的 lambda 类型 return 之间存在差异。传递给 .defer()
运算符的 Lambda 必须 return Rx 类型(在我们的例子中为 Single)。
最终实现(我做了一些修改使其编译成功):
data class User(val id: String)
data class System(val users: List<User>, val id: Long)
class CloudRepository {
fun getSystem(id: Long) = Single.just(System(mutableListOf(), id))
}
class SO63506574(
private val cloudRepository: CloudRepository
) {
fun execute(localSystem: System, delay: Long): Completable {
return Single.defer { cloudRepository.getSystem(localSystem.id) } // <-- defer
.repeatWhen { repeatHandler ->
repeatHandler.delay(delay, TimeUnit.SECONDS)
}
.takeUntil {
return@takeUntil it.users.count() > localSystem.users.count()
}
.ignoreElements()
}
}
并测试(~8 秒后成功):
class SO63506574Test {
@Test
fun testListeningEnds() {
val TEST_USER = User("UUID")
val TEST_SYSTEM = System(mutableListOf(), 10)
val repository = mockk<CloudRepository>()
val useCase = SO63506574(repository)
val testSystemGetResponse = System(mutableListOf(), 10)
every { repository.getSystem(10) } returnsMany listOf(
Single.just(testSystemGetResponse), // return the same amount of users as local system has
Single.just(testSystemGetResponse), // return the same amount of users as local system has
Single.just( // return the greater amount of users as local system has
testSystemGetResponse.copy(
users = listOf(
TEST_USER,
TEST_USER.copy(id = UUID.randomUUID().toString())
)
)
)
)
useCase.execute(
localSystem = TEST_SYSTEM,
delay = 3L
)
.test()
.await()
.assertComplete()
}
}
我正在尝试使用 Mockk 库测试多个服务器响应。类似于我在 this answer 中为 Mockito 找到的内容。
有我的示例 UseCase 代码,它每隔几秒就会重复调用以从远程服务器加载系统,当远程系统包含的用户多于本地时,它会停止 运行(onComplete
是已执行)。
override fun execute(localSystem: System, delay: Long): Completable {
return cloudRepository.getSystem(localSystem.id)
.repeatWhen { repeatHandler -> // Repeat every [delay] seconds
repeatHandler.delay(params.delay, TimeUnit.SECONDS)
}
.takeUntil { // Repeat until remote count of users is greater than local count
return@takeUntil it.users.count() > localSystem.users.count()
}
.ignoreElements() // Ignore onNext() calls and wait for onComplete()/onError() call
}
为了测试此行为,我正在使用 Mockk 库模拟 cloudRepository.getSystem()
方法:
@Test
fun testListeningEnds() {
every { getSystem(TEST_SYSTEM_ID) } returnsMany listOf(
Single.just(testSystemGetResponse), // return the same amount of users as local system has
Single.just(testSystemGetResponse), // return the same amount of users as local system has
Single.just( // return the greater amount of users as local system has
testSystemGetResponse.copy(
owners = listOf(
TEST_USER,
TEST_USER.copy(id = UUID.randomUUID().toString())
)
)
)
)
useCase.execute(
localSystem = TEST_SYSTEM,
delay = 3L
)
.test()
.await()
.assertComplete()
}
如您所见,我使用的是 returnsMany
答案,每次调用时应 return 不同的值。
主要问题是 returnsMany
return 每次都是相同的第一个值,并且 .takeUntil {}
永远不会成功,这意味着永远不会为此 Completable 调用 onComplete()
。如何使 returnsMany
return 在每次调用时具有不同的值?
您可能不了解 .repeatWhen()
的工作原理。您希望每次请求重复时都会调用 cloudRepository.getSystem(id)
。那是不正确的。重复订阅一直在模拟 Single
的同一个实例上完成 - 在您的情况下是第一个 Single.just(testSystemGetResponse)
。
如何确保每次都调用getSystem()
?将您的 Single 包装成 Single.defer()
。它类似于 Single.fromCallable()
但传递的 lambda 类型 return 之间存在差异。传递给 .defer()
运算符的 Lambda 必须 return Rx 类型(在我们的例子中为 Single)。
最终实现(我做了一些修改使其编译成功):
data class User(val id: String)
data class System(val users: List<User>, val id: Long)
class CloudRepository {
fun getSystem(id: Long) = Single.just(System(mutableListOf(), id))
}
class SO63506574(
private val cloudRepository: CloudRepository
) {
fun execute(localSystem: System, delay: Long): Completable {
return Single.defer { cloudRepository.getSystem(localSystem.id) } // <-- defer
.repeatWhen { repeatHandler ->
repeatHandler.delay(delay, TimeUnit.SECONDS)
}
.takeUntil {
return@takeUntil it.users.count() > localSystem.users.count()
}
.ignoreElements()
}
}
并测试(~8 秒后成功):
class SO63506574Test {
@Test
fun testListeningEnds() {
val TEST_USER = User("UUID")
val TEST_SYSTEM = System(mutableListOf(), 10)
val repository = mockk<CloudRepository>()
val useCase = SO63506574(repository)
val testSystemGetResponse = System(mutableListOf(), 10)
every { repository.getSystem(10) } returnsMany listOf(
Single.just(testSystemGetResponse), // return the same amount of users as local system has
Single.just(testSystemGetResponse), // return the same amount of users as local system has
Single.just( // return the greater amount of users as local system has
testSystemGetResponse.copy(
users = listOf(
TEST_USER,
TEST_USER.copy(id = UUID.randomUUID().toString())
)
)
)
)
useCase.execute(
localSystem = TEST_SYSTEM,
delay = 3L
)
.test()
.await()
.assertComplete()
}
}