如何使用MockK mock默认参数依赖注入字段的类方法?
问题根源
通过Mockk生成的Slack mock对象默认不会执行原类的构造函数,因此类内部的brokerSettings字段未被初始化,值为null。调用alert方法计算默认参数时直接访问非空类型的brokerSettings,就会触发空指针异常。
可行解决方案(按推荐优先级排序)
1. 测试Slack类自身逻辑时,直接构造真实实例
测试目标类自身逻辑时不应该mock目标类,仅mock其依赖BrokerSettings即可,完全规避mock带来的兼容性问题:
// 构造mock的依赖项 val mockBrokerSettings = mockk<BrokerSettings> { every { slack } returns mockk { every { responsible } returns "test-responsible" every { channels } returns listOf("test-alert-channel") } } // 直接构造真实的Slack实例 val slack = Slack(mockBrokerSettings) // 调用方法时默认参数会走原类正常逻辑,无NPE问题 slack.alert("测试告警消息")
2. 测试其他依赖Slack的类时,对alert方法预打桩
如果需要把Slack作为外部依赖mock注入,不需要触发原类的默认参数计算逻辑,直接对alert方法的所有调用场景打桩即可:
val mockSlack = mockk<Slack>() // 匹配alert方法的任意参数调用 every { mockSlack.alert(any(), any(), anyOrNull()) } just Runs // 注入mockSlack到待测试的业务类中即可正常测试
3. 必须mock Slack类且需保留原方法逻辑时,传入构造参数初始化mock实例
如果确实需要mock Slack类,同时要保留原方法的默认参数逻辑,可以在构造mock时传入构造参数,触发原类构造函数正常执行,完成brokerSettings的初始化:
// 先构造mock的BrokerSettings依赖 val mockBrokerSettings = mockk<BrokerSettings>(relaxed = true) // 构造mock Slack时传入构造参数 val mockSlack = mockk<Slack>( constructorArgs = arrayOf(mockBrokerSettings), relaxed = true ) // 此时调用带默认参数的alert方法不会触发NPE mockSlack.alert("测试告警消息")
额外说明
你之前将brokerSettings.slack修改为brokerSettings?.slack的方案之所以生效,是因为兼容了mock场景下brokerSettings为null的情况,但原类构造函数已经保证了brokerSettings非空,因此IDE会报不必要的可空操作警告,属于为了测试修改生产代码的妥协方案,不推荐使用。
内容的提问来源于stack exchange,提问作者Tim Baverstock
相关产品推荐
相关产品推荐

