Android/Kotlin:如何检测作为参数传递的函数是否被调用
检测作为参数传递的函数是否被调用的解决方案
当然有办法解决这个问题啦!在Kotlin中测试这类作为高阶函数参数传入的lambda是否被执行,最便捷的方式是借助Mocking库(比如MockK,对Kotlin的函数类型支持特别到位),如果不想引入第三方库,也可以手动实现简单的调用追踪逻辑。下面针对你的场景给出具体方案:
方案一:使用MockK(推荐)
MockK是专门为Kotlin设计的Mock库,能轻松追踪lambda的调用情况。
步骤1:添加MockK依赖
在你的项目构建文件(比如build.gradle.kts或build.gradle)中加入依赖:
// 针对JUnit 5 testImplementation("io.mockk:mockk:1.13.8") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.2")
步骤2:编写测试用例
针对你的updateWeight函数,我们可以为每个传入的lambda创建mock,然后在调用目标方法后,验证对应的mock是否被调用:
import io.mockk.every import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.Test import android.text.Editable import android.text.SpannableStringBuilder class WeightUpdateTest { @Test fun `当输入为空时,应调用showEmptyError`() { // 1. 创建各个lambda的mock val showEmptyErrorMock = mockk<() -> Unit>(relaxed = true) val showOutOfRangeErrorMock = mockk<() -> Unit>(relaxed = true) val dismissAlertDialogMock = mockk<() -> Unit>(relaxed = true) val updateAndShowMock = mockk<(Float) -> Unit>(relaxed = true) // 2. 创建空输入的Editable对象 val emptyInput = SpannableStringBuilder(" ") // 带空格的空字符串,trim后为空 // 3. 调用目标方法 updateWeight(emptyInput, showEmptyErrorMock, showOutOfRangeErrorMock, dismissAlertDialogMock, updateAndShowMock) // 4. 验证只有showEmptyError被调用 verify(exactly = 1) { showEmptyErrorMock() } verify(exactly = 0) { showOutOfRangeErrorMock() } verify(exactly = 0) { dismissAlertDialogMock() } verify(exactly = 0) { updateAndShowMock(any()) } } @Test fun `当输入超过300时,应调用showOutOfRangeError`() { val showEmptyErrorMock = mockk<() -> Unit>(relaxed = true) val showOutOfRangeErrorMock = mockk<() -> Unit>(relaxed = true) val dismissAlertDialogMock = mockk<() -> Unit>(relaxed = true) val updateAndShowMock = mockk<(Float) -> Unit>(relaxed = true) val overRangeInput = SpannableStringBuilder("350") updateWeight(overRangeInput, showEmptyErrorMock, showOutOfRangeErrorMock, dismissAlertDialogMock, updateAndShowMock) verify(exactly = 0) { showEmptyErrorMock() } verify(exactly = 1) { showOutOfRangeErrorMock() } verify(exactly = 0) { dismissAlertDialogMock() } verify(exactly = 0) { updateAndShowMock(any()) } } @Test fun `当输入在正常范围时,应调用dismissAlertDialog和updateAndShow`() { val showEmptyErrorMock = mockk<() -> Unit>(relaxed = true) val showOutOfRangeErrorMock = mockk<() -> Unit>(relaxed = true) val dismissAlertDialogMock = mockk<() -> Unit>(relaxed = true) val updateAndShowMock = mockk<(Float) -> Unit>(relaxed = true) val validInput = SpannableStringBuilder("75.5") val expectedValue = 75.5f updateWeight(validInput, showEmptyErrorMock, showOutOfRangeErrorMock, dismissAlertDialogMock, updateAndShowMock) verify(exactly = 0) { showEmptyErrorMock() } verify(exactly = 0) { showOutOfRangeErrorMock() } verify(exactly = 1) { dismissAlertDialogMock() } verify(exactly = 1) { updateAndShowMock(expectedValue) } } }
这里的relaxed = true表示mock会自动返回默认值(对于无返回值的lambda来说就是什么都不做),避免我们手动设置每个mock的行为。
方案二:手动实现调用追踪(无需第三方库)
如果不想引入MockK,你可以自己创建简单的类来记录函数是否被调用:
// 定义一个追踪类,用于记录函数调用状态 class CallTracker { var isCalled = false private set fun markCalled() { isCalled = true } } // 针对带参数的函数,可以扩展一个带参数记录的版本 class ParamCallTracker<T> { var lastCalledParam: T? = null private set var isCalled = false private set fun markCalled(param: T) { isCalled = true lastCalledParam = param } }
然后编写测试用例:
import org.junit.jupiter.api.Test import android.text.Editable import android.text.SpannableStringBuilder import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue class WeightUpdateManualTest { @Test fun `空输入时触发showEmptyError`() { val emptyErrorTracker = CallTracker() val outOfRangeTracker = CallTracker() val dismissTracker = CallTracker() val updateTracker = ParamCallTracker<Float>() val emptyInput = SpannableStringBuilder("") updateWeight( value = emptyInput, showEmptyError = { emptyErrorTracker.markCalled() }, showOutOfRangeError = { outOfRangeTracker.markCalled() }, dismissAlertDialog = { dismissTracker.markCalled() }, updateAndShow = { updateTracker.markCalled(it) } ) assertTrue(emptyErrorTracker.isCalled) assertFalse(outOfRangeTracker.isCalled) assertFalse(dismissTracker.isCalled) assertFalse(updateTracker.isCalled) } @Test fun `正常输入时触发dismiss和updateAndShow`() { val emptyErrorTracker = CallTracker() val outOfRangeTracker = CallTracker() val dismissTracker = CallTracker() val updateTracker = ParamCallTracker<Float>() val validInput = SpannableStringBuilder("80") val expectedValue = 80f updateWeight( value = validInput, showEmptyError = { emptyErrorTracker.markCalled() }, showOutOfRangeError = { outOfRangeTracker.markCalled() }, dismissAlertDialog = { dismissTracker.markCalled() }, updateAndShow = { updateTracker.markCalled(it) } ) assertFalse(emptyErrorTracker.isCalled) assertFalse(outOfRangeTracker.isCalled) assertTrue(dismissTracker.isCalled) assertTrue(updateTracker.isCalled) assertEquals(expectedValue, updateTracker.lastCalledParam) } }
这两种方案都能帮你精准验证不同输入场景下,哪个传入的函数会被执行。根据项目需求选择即可,MockK更适合复杂场景,手动追踪则更轻量。
内容的提问来源于stack exchange,提问作者kokilayaa
相关产品推荐
相关产品推荐

