You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中Mock全局属性无效,求CustomLogger测试解决方案

如何Mock Kotlin中CustomLogger的get()方法或init代码块

我遇到了和《Mock a "global" property in Kotlin》一文相同的问题,但无法在原问题下评论,因此重新提问。我尝试了文中所有解决方案均无效,目前正在为以下CustomLogger类编写测试用例,希望能Mock其get()方法或init代码块:

class CustomLogger constructor(val ctx: Context, embEnabled: Boolean = false) : Logger {

private val loggers = arrayListOf<Logger>()
fun get() = loggers

init {
    if (embEnabled)
        loggers.add(Emb(ctx))
    if (BuildConfig.DEBUG)
        loggers.add(DebugLogger(ctx))
}

override fun logError(t: Throwable, msg: String?) {
    loggers.forEach { logger ->
        logger.logError(t, msg)
    }
 }
}

方案1:重构类设计(最推荐)

直接调整CustomLogger的构造逻辑,把依赖的Logger实例作为参数传入,而非在init代码块中硬创建。这样测试时可以直接传入Mock对象,完全避开原有的初始化逻辑:

// 修改后的CustomLogger
class CustomLogger constructor(
    val ctx: Context,
    private val loggers: List<Logger> = emptyList()
) : Logger {

    // 保留原构造方法做兼容(可选)
    constructor(ctx: Context, embEnabled: Boolean = false) : this(
        ctx,
        buildList {
            if (embEnabled) add(Emb(ctx))
            if (BuildConfig.DEBUG) add(DebugLogger(ctx))
        }
    )

    override fun logError(t: Throwable, msg: String?) {
        loggers.forEach { it.logError(t, msg) }
    }
}

测试示例(以MockK为例):

val mockLogger1 = mockk<Logger>()
val mockLogger2 = mockk<Logger>()
val customLogger = CustomLogger(mockk<Context>(), listOf(mockLogger1, mockLogger2))

// 验证logError调用逻辑
customLogger.logError(Throwable(), "test")
verify {
    mockLogger1.logError(any(), any())
    mockLogger2.logError(any(), any())
}

方案2:修改BuildConfig.DEBUG值(针对调试日志场景)

如果只是想阻止DebugLogger被添加到loggers中,可以通过反射修改BuildConfig.DEBUG的取值:

// 反射修改BuildConfig.DEBUG
val debugField = BuildConfig::class.java.getField("DEBUG")
debugField.isAccessible = true
debugField.setBoolean(null, false)

执行这段代码后,CustomLogger的init代码块中就不会添加DebugLogger了。

方案3:部分Mock或反射修改loggers字段(无法修改原类时用)

如果不能改动原类代码,可通过部分Mock工具(如MockK的spyk)覆盖get()方法的返回值,或者直接反射修改private的loggers字段:

方式A:Mock get()方法

val mockLogger = mockk<Logger>()
val customLogger = spyk(CustomLogger(mockk<Context>())) {
    on { get() } returns listOf(mockLogger)
}

// 验证logError调用
customLogger.logError(Throwable(), "test")
verify { mockLogger.logError(any(), any()) }

方式B:反射修改loggers字段

因为init代码块在对象创建时就已执行,若要替换已初始化的loggers列表,可直接反射修改:

val customLogger = CustomLogger(mockk<Context>())
val mockLogger = mockk<Logger>()

// 反射修改private的loggers字段
val loggersField = CustomLogger::class.java.getDeclaredField("loggers")
loggersField.isAccessible = true
loggersField.set(customLogger, arrayListOf(mockLogger))

// 验证调用逻辑
customLogger.logError(Throwable(), "test")
verify { mockLogger.logError(any(), any()) }

内容的提问来源于stack exchange,提问作者Jitendra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 18:05:25