在模拟 class 中捕获具有通用结果的暂停 lambda 参数

Capture suspend lambda parameter with generic result in a mockked class

我想知道是否可以使用 MockK 和 JUnit5 捕获具有通用结果的暂停 lambda。 我尝试了几种方法,最近我在尝试 运行 测试时遇到 KotlinNullPointerException。

这是代码,包含完整的 class 依赖项和测试:

class Test {
data class Result<out T>(val status: ResultStatus, val data: T?, val exception: Exception?) {
    companion object {
        fun <T> success(data: T?): Result<T> {
            return Result(ResultStatus.SUCCESS, data, null)
        }

        fun <T> error(exception: Exception): Result<T> {
            return Result(ResultStatus.ERROR, null, exception)
        }

        fun <T> loading(data: T? = null): Result<T> {
            return Result(ResultStatus.LOADING, data, null)
        }
    }
}

class Dep1() {
    fun <R> methodToMock(viewModelScope: CoroutineScope, block: suspend CoroutineScope.() -> R): MutableLiveData<Result<R>> {
        val result = MutableLiveData<Result<R>>()
        result.value = Result.loading()

        viewModelScope.launch {
            try {
                var asyncRequestResult: R? = null
                withContext(Dispatchers.Default) {
                    asyncRequestResult = block()
                }
                result.value = Result.success(asyncRequestResult)
            } catch (cancellationException: CancellationException) {
            } catch (exception: Exception) {
                result.value = Result.error(exception)
            }
        }

        return result
    }
}

class Dep2() {
    fun methodToAssert(): Boolean {
        println("Called")
        return true
    }
}

class ClassToTest(private val dep1: Dep1, private val dep2: Dep2) {
    fun methodToCall(coroutineScope: CoroutineScope): MutableLiveData<Result<Boolean>> {
        return dep1.methodToMock(coroutineScope) {
            dep2.methodToAssert()
        }
    }
}

private val dep1: Dep1 = mockk()
private val dep2: Dep2 = mockk(relaxed = true)
private val mViewModelScope: CoroutineScope = GlobalScope

@Test
fun `Check if is calling the required methods correctly`() {
    val classToTest = ClassToTest(dep1, dep2)

    val transactionLambda = slot<suspend CoroutineScope.() -> Boolean>()
    coEvery { dep1.methodToMock(mViewModelScope, capture(transactionLambda)) } coAnswers {
        MutableLiveData(Result.success(transactionLambda.captured.invoke(mViewModelScope)))
    }

    classToTest.methodToCall(mViewModelScope)

    verify { dep2.methodToAssert() }
}

}

如果有人也遇到这个问题,我可以使用 every 而不是 coEvery 并调用 coInvoke() 插槽方法来解决它:

every { dep1.methodToMock(mViewModelScope, capture(transactionLambda)) } answers  {
    MutableLiveData(Result.success(transactionLambda.coInvoke(mViewModelScope)))
}