Tim Baverstock
Tim Baverstock

Reputation: 568

How to mockk when a method's default parameters need an injected field?

My class has a method with default parameters whose values are derived from an injected parameter:

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

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

I can construct a mockk<Slack>, but when my test tries to invoke alert(message) on it, relying on the default parameters, I get a null pointer exception in alert$default.

If I change brokerSettings.slack above to brokerSettings?.slack, then it works - but warns me that I don't need the extra ?.

I tried mocking the brokerSettings field's getter:

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

but [email protected] fails to compile if the field is private and throws an NPE just like the original problem if it's public, and this getProperty fails with a reflection error.

I'm bodging for now by setting the defaults to null and using ?: to conditionally compute the defaults inside the function, but I'd prefer not to do that.

Any ideas?

Upvotes: 0

Views: 5183

Answers (1)

Abacus
Abacus

Reputation: 19451

This is an edit/complete rewrite of my first answer. I obviously did pay too little attention to the real problem.

What you need to do is:

  • create a mock for the BrokerSettings instance
  • configure the mock to not fail on the slack getter
  • create a real Slack instance passing in the mock
  • wrap that Slack instance with a spy
  • configure that spy to not do anything real in the alert function
  • put the spy into your class under test
  • call the method you want to test
  • verify the slack.alert call

In 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()) }

    }
}

Upvotes: 1

Related Questions