2

In Kotlin,, we could have function object and pass in as function parameter.

  1. How to create unit test to test the function object logic? (e.g. funcParam below)
  2. How to unit test the function that has function parameter? (e.g. functionWithFuncParam below) - i.e. can I create a Mock for funcParam?

    class MyClass1(val myObject: MyObject) {
        val funcParam = fun (num: Int): Int {
            return num * num
        }
    
        fun myFunctionOne() {
            myObject.functionWithFuncParam(funcParam)
        }
    }
    
    class MyObject () {
        fun functionWithFuncParam(funcParam: (Int) -> Int) {
            println(funcParam(32))
        }
    }
    
Elye
  • 53,639
  • 54
  • 212
  • 474

1 Answers1

3

Assuming funcParam is public intentionally you can test it as any other method:

class MyClass1Tests {
    val sut = MyClass1(MyObject())

    @Test
    fun `funcParam multiplies input`() {
        assertThat(sut.funcParam(4), equalTo(16))
        assertThat(sut.funcParam(1), equalTo(1))
        assertThat(sut.funcParam(0), equalTo(0))
        assertThat(sut.funcParam(-10), equalTo(100))
    }
}

If funcParam is private you shouldn't test its behavior directly but only through public interface of it's containing class.

When testing functionWithFuncParam you can easily supply a stub implementation of (Int) -> Int:

class MyObjectTests {
    val outContent = ByteArrayOutputStream().apply {
        System.setOut(PrintStream(this))
    }
    val sut = MyObject()
    @Test
    fun `functionWithFuncParam prints function output `() {
        sut.functionWithFuncParam { 12345678  }
        assertThat(outContent.toString(), containsString("12345678"))
    }
}

If you'd like to test MyClass1 interaction with MyObject one way is to use interface implemented MyObject by in MyClass1. Usually the best choice if 2 classes are distinct collaborators in a sense that they have separate mostly unrelated behaviour:

interface FunctionalObj {
    fun functionWithFuncParam(funcParam: (Int) -> Int)
}
class MyClass1(val myObject: FunctionalObj) {
//omitted for brevity
}
class MyClass1Tests {
    var params = mutableListOf<(Int)->Int>()
    val sut = MyClass1(object: FunctionalObj {
        override fun functionWithFuncParam(funcParam: (Int) -> Int) { params.add(funcParam) }
    })

    @Test
    fun `myFunctionOne calls delegate`() {
        sut.myFunctionOne()
        assertThat(params.size, equalTo(1))
        assertThat(params[0], equalTo(sut.funcParam))//only if `funcParam` is public
    }
}

If MyClass1 and MyObject interaction is more complex (i.e. involves more calls both queries and commands) it would imply that they are peers working together closely. In such case, using mocks can lead to brittle and hard to write tests.

miensol
  • 39,733
  • 7
  • 116
  • 112
  • Thanks miensol. I'm thinking of if I am mocking myObject, and want to test that `myFunctionOne` to verify it is calling `functionWithFuncParam`, is there a way I could supply "any()" for the Function Param? – Elye Jun 07 '16 at 05:26
  • @Elye see my update. What is passed to `myObject` appears to me like an implementation detail - and it's generally a bad idea to rely on them in tests. One not test their behavior together and not rely on mocks? – miensol Jun 07 '16 at 05:54