kotlin - mock 验证 lambda 在 mock 中传递

标签 kotlin mockk

我正在尝试测试方法getSongsList

class SongsRemoteDataSource @Inject constructor(
    private val resultParser: ResultParser,
    private val songsService: SongsService
) {

    suspend fun getSongsList(query: String): Result<SongsResponse> =
        resultParser.parse { songsService.getSongsList(query) }
}
基本上我正在尝试测试是否使用正确的 lambda 作为参数调用了模拟
class SongsRemoteDataSourceTest {

    @RelaxedMockK
    private lateinit var resultParser: ResultParser
    @RelaxedMockK
    private lateinit var songsService: SongsService

    private lateinit var songsRemoteDataSource: SongsRemoteDataSource

    @Before
    fun setUp() {
        MockKAnnotations.init(this)
        songsRemoteDataSource = SongsRemoteDataSource(resultParser, songsService)
    }

    @Test
    fun getSongsList() = runBlockingTest {
        val query = "query"

        songsRemoteDataSource.getSongsList(query)

        coVerify { resultParser.parse { songsService.getSongsList(query) } }
    }
}
但测试失败
java.lang.AssertionError: Verification failed: call 1 of 1: ResultParser(resultParser#1).parse(eq(continuation {}), any())). Only one matching call to ResultParser(resultParser#1)/parse(Function1, Continuation) happened, but arguments are not matching:
[0]: argument: continuation {}, matcher: eq(continuation {}), result: -
[1]: argument: continuation {}, matcher: any(), result: +
结果解析器
class ResultParser @Inject constructor() {

    suspend fun <T> parse(call: suspend () -> Response<T>): Result<T> {
        ...
    }
}
歌曲服务
interface SongsService {

    @GET("search")
    suspend fun getSongsList(
        @Query("term") query: String,
        @Query("mediaType") mediaType: String = "music"
    ): Response<SongsResponse>
}
我不明白为什么它会失败。我究竟做错了什么?

最佳答案

函数总是按身份进行比较
该语言不知道如何将一个函数的内容与另一个函数的内容进行比较。在不同位置创建的两个 lambda 函数,即使它们做完全相同的事情,也不会被视为彼此相等。
你可以用一个简单的例子来证明这一点:

val a = { "Hello, World!" }
val b = { "Hello, World!" }
println(a == b) // prints 'false'
您的 verify调用失败是因为您实际上有两个不同的 lambda 函数,即使它们包含相同的代码。在 SongsRemoteDataSource 中创建的 lambda是与 SongsRemoteDataSourceTest 中创建的对象不同的对象,因此 MockK 认为它们彼此不相等。
测试 lambda 函数内容的唯一真正方法是运行它并查看它的作用。为此,您有几个选择。
使用answers运行 lambda 函数
解决此问题的一种方法是配置模拟 ResultParser始终运行它接收到的 lambda 函数。
coEvery { 
    resultParser.parse(any()) 
} coAnswers { 
    firstArg<(suspend () -> Response<SongsResponse>)>().invoke() 
}
现在,每次ResultParser被调用时,它将立即运行它作为输入接收到的任何 lambda 函数。然后,在您调用 getSongsList 之后,您可以验证 SongsService被称为。
songsRemoteDataSource.getSongsList(query)
coVerify { 
    resultParser.parse(any())
    songsService.getSongsList(query)
}
捕获 lambda 函数并自己运行
如果您想更加明确,可以改为捕获 lambda。这使您可以将 lambda 函数分配给一个变量,您可以在其中做任何您想做的事情。您仍然无法比较它是否相等,但您仍然可以运行它并测试它的作用。
首先你创建一个 slot它将保存捕获的功能。然后你使用 coVerify将插槽作为参数匹配器。
val lambdaSlot = slot<(suspend () -> Response<SongsResponse>)>()

songsRemoteDataSource.getSongsList(query)
coVerify { resultParser.parse(capture(lambdaSlot)) }

lambdaSlot.captured.invoke() // runs the lambda function
coVerify { songsService.getSongsList(query) }

关于kotlin - mock 验证 lambda 在 mock 中传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63653728/

相关文章:

Android WorkManager 用例

android - 如何使用 mockk 模拟 Build.VERSION.SDK_INT

spring - 如何在@InjectMockKs 测试实例中注入(inject)实现列表?

android - MockK 模拟 ViewModel 的 savedStateHandle.getLiveData()

kotlin - 此表达式将在后续版本中解析为 Int。请添加明确的约定调用

java - Spring R2DBC:如果id为零,则保存新实体

kotlin - Kotlin 编程语言中的析构函数

Android:Kotlin TypeCastException: null 无法转换为非空类型 kotlin.String

kotlin - mockk的allAny()是如何使用的

android - Dagger 不注入(inject) Activity