如何使用 MockK 模拟构建器以使构建器函数可交换?

Hen*_*ing 7 android unit-testing mocking mockk

我得到了一些代码,我想使用 MockK 作为模拟库来测试它们:

fun loadImage(imageLoader: coil.ImageLoader) {
    val request = ImageRequest.Builder(imageView.context)
        .data(url)
        .crossfade(true)
        .build()
    imageLoader.enqueue(request)
}
Run Code Online (Sandbox Code Playgroud)

我可以使用分层方法来模拟它:


val request: ImageRequest = mockk()
val imageLoader: coil.ImageLoader = mockk(relaxed = true)
mockkConstructor(ImageRequest.Builder::class)
every { anyConstructed<ImageRequest.Builder>().data(any()) } returns mockk {
    every { crossfade(any<Boolean>()) } returns mockk {
        every { build() } returns request
    }
}

loadImage(imageLoader)

verify { imageLoader.enqueue(request) }
Run Code Online (Sandbox Code Playgroud)

所以这是有问题的,因为我还测试了构建器函数的调用顺序。当我切换.data.crossfade调用时,测试会中断,而实现仍然有效。

有更好的方法来解决这个问题吗?

小智 1

我认为您正在寻找的是answers { this.self as ImageRequest.Builder }而不是returns mockk().

然而,对于多个构建器函数,由于多次调用 anyConstructed...answers{},它会变得相当乏味/重复。如果你在某处定义这个函数:

    inline fun <reified T : Any> mockBuilder(crossinline block: MockKMatcherScope.(T) -> T) {
        every { block(anyConstructed<T>()) } answers {
            this.self as T
        }
    }
Run Code Online (Sandbox Code Playgroud)

你可以这样使用它:

    mockkConstructor(ImageRequest.Builder::class)
    every { anyConstructed<ImageRequest.Builder>().build() } returns mockk()
    mockBuilder<ImageRequest.Builder> { it.data(any()) }
    mockBuilder<ImageRequest.Builder> { it.crossfade(any<Boolean>()) }
    ...
Run Code Online (Sandbox Code Playgroud)

如果您想知道如何构建 ImageRequest,它也可以用于捕获。