【问题标题】:How to mockk when a method's default parameters need an injected field?当方法的默认参数需要注入字段时如何模拟?
【发布时间】:2021-12-09 04:08:52
【问题描述】:

我的类有一个带有默认参数的方法,其值来自注入的参数:

class Slack @Inject constructor(
  private val brokerSettings: BrokerSettings
) {

  fun alert(
    message: String,
    responsible: String = brokerSettings.slack?.responsible ?: "<!here>",
    channels: List<String>? = brokerSettings.slack?.channels
  ) {
   ...
  }
}

我可以构造一个mockk&lt;Slack&gt;,但是当我的测试尝试在其上调用alert(message) 时,依赖于默认参数,我在alert$default 中得到一个空指针异常。

如果我将上面的 brokerSettings.slack 更改为 brokerSettings?.slack,那么它可以工作 - 但警告我不需要额外的 ?

我尝试模拟 brokerSettings 字段的吸气剂:

val slack = mockk<Slack>(relaxed = true)
{
//    every { this@mockk.brokerSettings } answers { BrokerSettings() }
//    every { this getProperty "brokerSettings" } propertyType BrokerSettings::class answers { BrokerSettings() }
}

但是如果字段是私有的,this@mockk.brokerSettings 将无法编译,并且如果它是公共的,则会像原始问题一样抛出 NPE,并且 this getProperty 会失败并出现反射错误。

我暂时将默认值设置为null 并使用?: 有条件地计算函数内部的默认值,但我不想这样做。

有什么想法吗?

【问题讨论】:

    标签: kotlin nullpointerexception mocking mockk default-parameters


    【解决方案1】:

    这是对我的第一个答案的编辑/完全重写。我显然对真正的问题关注得太少了。

    你需要做的是:

    • BrokerSettings 实例创建一个模拟
    • 将模拟配置为不会在 slack getter 上失败
    • 创建一个真实 Slack 实例传入模拟
    • 用间谍包裹Slack 实例
    • 将间谍配置为在 alert 函数中不做任何实际操作
    • 将间谍放入您的测试类中
    • 调用你要测试的方法
    • 验证slack.alert 调用

    在 Kotlin 中:

    class SlackUsingClass(private val slack: Slack) {
        fun doIt(message: String) {
            slack.alert(message)
        }
    }
    
    import io.mockk.Runs
    import io.mockk.every
    import io.mockk.just
    import io.mockk.mockk
    import io.mockk.spyk
    import io.mockk.verify
    import org.junit.jupiter.api.Test
    
    class SlackUsingClassTest {
    
        @Test
        fun `should call slack`() {
    
            val brokerSettings = mockk<BrokerSettings>()
            every { brokerSettings.slack } returns null
    
            val slack = spyk(Slack(brokerSettings))
            every { slack.alert(any(), any(), any())} just Runs
    
            val slackUsingClass = SlackUsingClass(slack)
    
            slackUsingClass.doIt("test")
    
            verify { slack.alert("test", any(), any()) }
    
        }
    }
    

    【讨论】:

    • 正如我上面所说,我在模拟 Slack 以测试不同的类,当其他类尝试执行 slack.alert(message) 时,Slack.alert 会尝试评估 brokerSettings 以确定一个的默认值它的参数,并且 alert$default 在定义参数默认值的行上获得一个 NPE。因此,在模拟调用中为这些参数提供 any() 没有任何区别,因为发生 NPE 是因为参数的默认值是尝试基于从未设置的构造函数参数评估表达式(因为 mockk 没有'不知道设置它)。
    • 我重写了我的答案,之前我对问题的研究不够彻底。
    猜你喜欢
    • 2011-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-13
    • 2012-07-20
    • 1970-01-01
    相关资源
    最近更新 更多