2

I am trying to perform a chain call within my unit test using the mockk library within Kotlin. Below is my code:

@MockK
lateinit var crypto: Crypto

@Before
fun setUp() {
    MockKAnnotations.init(this, relaxUnitFun = true)
}

@Test
fun testCryptoFunc() {
    // Given
    // When
    every { crypto.sayHello() } returns "gg" // This works
    every { crypto.sayHelloTwice("w").sayHello() } returns "gg" //sayHello() is unresolved

    // Then
    //val c = crypto.sayHelloTwice("ss")
    //print("rr")
}

And my implementation code:

fun sayHello(): String {
    return "hello"
}

fun sayHelloTwice(a: String): String {
    return sayHello() + a
}

I am trying to stub the inner call (sayHello()) but I am getting unresolved reference error. According to the Mockk documentation for chain calls, it says this should be valid.

I've tried cleaning and rebuilding (but running into the compilation error). Tried restarting the IDE. Tried invalidating caches and restarting.

Is there anything that I'm missing/doing wrong?

References:

https://mockk.io/#chained-calls

https://stackoverflow.com/a/54347784/1342086

rj2700
  • 1,770
  • 6
  • 28
  • 55
  • Strings are not mockable – oleksiyp Mar 20 '19 at 14:17
  • I may be too naiive to realize but I was able to mock the sayHello() properly. Regardless, even if I change the return type for these methods to data classes, it's still running into the same issue. – rj2700 Mar 20 '19 at 14:41
  • Weird, can you submit github issue with logback TRACE level enabled logs – oleksiyp Mar 20 '19 at 15:23

1 Answers1

0

A couple of things here.

First, in a unit test, you shouldn't be testing a mock. You should be testing the concrete class. Otherwise you're just testing Mockk works as expected. So this will be a better test:

class Test {
    private lateinit var crypto: Crypto

    @Before
    fun setUp() {
        crypto = Crypto()
    }

    @Test
    fun testCryptoFunc() {
        // Given
        val textToAdd = "gg"
        val expected = "hello$textToAdd"

        // When
        val actual = crypto.sayHelloTwice(textToAdd)

        // Then
        assertEquals(expected, actual)
    }
}

But let's say you want to mock Crypto as part of another test. Your sayHelloTwice function returns a String. a String doesn't have a sayHello function. One way to approach this is to make sayHello and sayHelloTwice extension functions on String. Then, you can stub sayHelloTwice. Once it returns a value, you can stub sayHello to process that value. Remember sayHelloTwice won't really be calling sayHello, since sayHelloTwice is now a stub. So you would have to call sayHello yourself:

class Test {
    @MockK
    lateinit var crypto: Crypto

    @Before
    fun setUp() {
        MockKAnnotations.init(this, relaxUnitFun = true)
    }

    @Test
    fun testCryptoFunc() {
        with (crypto) {
            // Given
            every { "tt".sayHelloTwice("aa") } returns "mm"
            every { "mm".sayHello() } returns "success"

            // When
            val c = "tt".sayHelloTwice("aa").sayHello()

            // Then
            println(c)
        }
    }
}

class Crypto {
    fun String.sayHello(): String = "hello$this"

    fun String.sayHelloTwice(a: String): String =
        "$this$a".sayHello()
}
Eran Boudjnah
  • 1,228
  • 20
  • 22