模拟验证lambda是否在模拟中传递

问题描述

我正在尝试测试方法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: +

ResultParser

class ResultParser @Inject constructor() {

    suspend fun <T> parse(call: suspend () -> Response<T>): Result<T> {
        ...
    }
}

SongsService

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中创建的lambda是不同的对象,因此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) }

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...