如何使用MockK Stub派生类实例中基类的protected函数?
如何用MockK Stub基类的protected函数?
现有代码
基类代码
abstract class BaseNotification<T: Any> { protected abstract fun convertToJson(message: T): JSONObject? companion object { @JvmStatic @Throws(JSONException::class) protected fun convertMapToJson(dataMap: Map<String, String>): JSONObject { val result = JSONObject() for ((key, value) in dataMap) { result.put(key, value) } return result } } }
派生类代码
class DerivedNotification private constructor() : BaseNotification<Intent>() { @VisibleForTesting internal fun doConvertJson(message: Intent) : JSONObject? = convertToJson(message) fun getMap(bundle: Intent): MutableMap<String, String> { val bundleToMap: MutableMap<String, String> = HashMap() for (key in bundle.keySet()) { val value = bundle.getString(key) if (value != null) { bundleToMap[key] = value } } return bundleToMap } override fun convertToJson(message: Intent): JSONObject? { val bundleToMap: MutableMap<String, String> = getMap(message) var jsonObject: JSONObject? = null jsonObject = try { convertMapToJson(bundleToMap) } catch (e: JSONException) { return null } return jsonObject } }
编译失败的测试代码
@Test fun test_convertToJson() { val derivedNotification = spyk(DerivedNotification()) every { derivedNotification.getMap(any()) } returns mapWithData val intentMock = mockk<Intent>(relaxed = true) // Mock the doConvertMapToJson method to throw a JSONException every { derivedNotification.convertMapToJson(any()) } throws JSONException("+++ !!! exp") //test val jsonObj = derivedNotification.doConvertJson(intentMock) // verify assertNull(jsonObj) }
问题核心
convertMapToJson是基类伴生对象的protected函数,测试类无法直接访问该函数,且原测试代码错误地试图在派生类实例上调用伴生对象的函数,导致编译失败。
解决方案
方法1:Mock伴生对象的protected函数(推荐)
利用MockK的mockkStaticAPI来mock基类的伴生对象,直接Stub目标protected函数的行为:
@Test fun test_convertToJson() { val derivedNotification = spyk(DerivedNotification()) every { derivedNotification.getMap(any()) } returns mapWithData val intentMock = mockk<Intent>(relaxed = true) // Mock基类伴生对象的protected函数 mockkStatic(BaseNotification.Companion::class) every { BaseNotification.convertMapToJson(any()) } throws JSONException("+++ !!! exp") // 执行测试 val jsonObj = derivedNotification.doConvertJson(intentMock) // 验证结果 assertNull(jsonObj) // 清理mock unmockkStatic(BaseNotification.Companion::class) }
方法2:通过反射调整可见性(兼容特殊场景)
如果无法直接mock伴生对象,可以通过反射解除函数的protected限制,再结合MockK进行Stub:
@Test fun test_convertToJson() { val derivedNotification = spyk(DerivedNotification()) every { derivedNotification.getMap(any()) } returns mapWithData val intentMock = mockk<Intent>(relaxed = true) // 通过反射获取伴生对象的protected函数 val convertMethod = BaseNotification.Companion::class.java .getDeclaredMethod("convertMapToJson", Map::class.java) convertMethod.isAccessible = true // Mock伴生对象的函数行为 mockkStatic(BaseNotification.Companion::class) every { convertMethod.invoke(BaseNotification.Companion, any<Map<String, String>>()) } throws JSONException("+++ !!! exp") val jsonObj = derivedNotification.doConvertJson(intentMock) assertNull(jsonObj) unmockkStatic(BaseNotification.Companion::class) }
关键说明
- 注意区分实例protected函数和伴生对象protected函数:本次场景中的目标函数属于伴生对象,必须mock伴生对象而非派生类实例。
mockkStatic用于处理静态/伴生对象的mock,测试结束后建议调用unmockkStatic清理,避免影响其他测试用例。
内容的提问来源于stack exchange,提问作者lannyf
相关产品推荐
相关产品推荐

