测试RxJava重复时带Mockk返回很多

问题描述

我正在尝试使用Mockk库测试多个服务器响应。就像我在this answer中找到的Mockito一样。

我有一个示例UseCase代码,每隔几秒钟重复一次从远程服务器加载系统的调用,并且当远程系统包含的用户多于本地用户时,它将停止运行(@H_502_3@onComplete)。

@H_502_3@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库中模拟@H_502[email protected]stem()方法

@H_502_3@@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()
}

如您所见,我正在使用@H_502_3@returnsMany答案,该答案应在每次通话时返回不同的值。

主要问题是@H_502_3@returnsMany每次都返回相同的第一个值,而@H_502_3@.takeuntil {}永不成功,这意味着从未为该Completable调用@H_502_3@onComplete()。如何使@H_502_3@returnsMany在每次通话中返回不同的值?

解决方法

您可能不了解.repeatWhen()的工作原理。您希望每次请求重复时都会调用cloudRepository.getSystem(id)。那是不对的。重复预订始终在模拟Single的同一实例上进行–在您的情况下,第一个Single.just(testSystemGetResponse)

如何确保getSystem()每次都被调用?将您的Single包装到Single.defer()中。它类似于Single.fromCallable(),但是所传递的lambda的返回类型之间存在差异。传递给.defer()运算符的Lambda必须返回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()
    }
}