使用 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()
    }
}