Kotlin Anonymous Function Parameter Unit Testing - unit-testing

As per Kotlin Unit Testing for Function Parameter and Object, we could test the function variable funcParam, as it is an object function variable.
However if code is written using anonymous/inlining function parameter (which is a very nice Kotlin feature, that allow us to eliminate unnecessary temp variable for it)...
class MyClass1(val myObject: MyObject, val myObject2: MyObject2) {
fun myFunctionOne() {
myObject.functionWithFuncParam{
num: Int ->
// Do something to be tested
myObject2.println(num)
}
}
}
class MyObject () {
fun functionWithFuncParam(funcParam: (Int) -> Unit) {
funcParam(32)
}
}
How to write my unit test that test this part of code?
num: Int ->
// Do something to be tested
myObject2.println(num)
Or the inlining of the function parameter (as above) is something not good for unit testing, and hence should be avoided?

After a while discover the way to test it is to use Argument Captor.
#Test
fun myTest() {
val myClass1 = MyClass1(mockMyObject, mockMyObject2)
val argCaptor = argumentCaptor<(Int) -> Unit>()
val num = 1 //Any number to test
myClass1.myFunctionOne()
verify(mockMyObject).functionWithFuncParam(argCaptor.capture())
argCaptor.value.invoke(num)
// after that you could verify the content in the anonymous function call
verify(mockMyObject2).println(num)
}
For more info, refer to https://medium.com/#elye.project/how-to-unit-test-kotlins-private-function-variable-893d8a16b73f#.1f3v5mkql

Related

Assert mock call with argument assigned in where block in Spock

I have a method that among others generates an identifier, sends it to some external dependency and returns it. I want to have a unit test that tests if the same value that was sent there is returned.
Let's say that the test and the code look like this:
def "test"() {
given:
def testMock = Mock(TestMock)
and:
def x
when:
def testClass = new TestClass()
x = testClass.callMethod(testMock)
then:
1 * testMock.method(x)
}
static interface TestMock {
void method(double x)
}
static class TestClass {
double callMethod(TestMock mock) {
double x = Math.random()
mock.method(x)
return x
}
}
The code works correct, however the test fails with a message:
One or more arguments(s) didn't match:
0: argument == expected
| | |
| | null
| false
0.5757686318956925
So it looks like the mock check in then is done before the value is assigned in when block.
Is there a way to make Spock assign this value before he checks the mock call? Or can I do this check in a different way?
The only idea I have is to inject an id-generator to the method (or actually to the class) and stub it in the test, but it would complicate the code and I would like to avoid it.
I fixed code example according to kriegaex comment.
Fixing the sample code
Before we start, there are two problems with your sample code:
1 * testMock(x) should be 1 * testMock.method(x)
callMethod should return double, not int, because otherwise the result would always be 0 (a double between 0 and 1 would always yield 0 when converted to an integer).
Please, next time make sure that your sample code not only compiles but also does the expected thing. Sample code is only helpful if it does not have extra bugs, which a person trying to help you needs to fix first before being able to focus on the actual problem later on.
The problem at hand
As you already noticed, interactions, even though lexically defined in a then: block, are transformed in such a way by Spock's compiler AST transformations, that they are registered on the mock when the mock is initialised. That is necessary because the mock must be ready before calling any methods in the when: block. Trying to directly use a result only known later while already executing the when: block, will cause the problem you described. What was first, chicken or egg? In this case, you cannot specify a method argument constraint, using the future result of another method calling the mock method in the constraint.
The workaround
A possible workaround is to stub the method and capture the argument in the closure calculating the stub result, e.g. >> { args -> arg = args[0]; return "stubbed" }. Of course, the return keyword is redundant in the last statement of a closure or method in Groovy. In your case, the method is even void, so you do not need to return anything at all in that case.
An example
I adapted your sample code, renaming classes, methods and variables to more clearly describe which is what and what is happening:
package de.scrum_master.stackoverflow.q72029050
import spock.lang.Specification
class InteractionOnCallResultTest extends Specification {
def "dependency method is called with the expected argument"() {
given:
def dependency = Mock(Dependency)
def randomNumber
def dependencyMethodArg
when:
randomNumber = new UnderTest().getRandomNumber(dependency)
then:
1 * dependency.dependencyMethod(_) >> { args -> dependencyMethodArg = args[0] }
dependencyMethodArg == randomNumber
}
}
interface Dependency {
void dependencyMethod(double x)
}
class UnderTest {
double getRandomNumber(Dependency dependency) {
double randomNumber = Math.random()
dependency.dependencyMethod(randomNumber)
return randomNumber
}
}
Try it in the Groovy Web Console.

How do we verify method that has lambda function as its argument?

So I have this main() method that calls printResult() method and the printResult() has a lambda argument on second parameter like this:
class SomeClass() {
fun main(value: Int) {
printResult(value){ it ->
it + it
}
}
fun printResult(value: Int, sum: (Int) -> Int) {
val result = sum(value)
println(result)
}
}
Then in the unit test, I want to verify that every time the main() method is called, the printResult() should be invoked as well. So I write the unit test like this:
#Test
fun testMain_shouldInvokePrintResult() {
someClass.main(10)
verify(someClass).printResult(10){ any() }
}
I don't know what argument I should pass for printResult() method, that's why I used any(). But when I run the test, the compiler said:
Argument(s) are different! Wanted:
someClass.main(
10,
() (kotlin.Exception /* = java.lang.Exception */) -> kotlin.Unit
);
-> at com.mydomain.test.testMain_shouldInvokePrintResult(SomeClassTest.kt:49)
Actual invocations have different arguments:
someClass.main(
10,
() (kotlin.Exception /* = java.lang.Exception */) -> kotlin.Unit
);
-> at com.mydomain.SomeClass.printResult$lambda-27(SomeClass.kt:20)
So the compiler basically said different arguments but showed me no different on both invocation. What should I do? any help would be appreciated..
It solved. So my colleagues told me to use any() inside parenthesis instead of the curly brackets. So it would be like this:
#Test
fun testMain_shouldInvokePrintResult() {
someClass.main(10)
verify(someClass).printResult(10, any())
}

How to use kotlin functional programming to access a property

I currently have the following function:
fun createMask(mask : String){
val ssnField : mywidgets.SSNField = findViewById (R.id.editTextText)
ssnField.hint = mask
}
To unit test this I want to wrap the untestable code within createMask into a closure. (The untestable code is the view layer logic that's difficult to instantiate and execute in a unit test.) Here is what I want to do in pseudo code:
createMask(closure, mask : String){
closure = mask // closure function returns pointer to property (depending on closure return type, might need to use setter: closure.set(mask))
}
With the above, the caller then does:
fun caller(){
createMask((){
val ssnField : mywidgets.SSNField = findViewById (R.id.editTextText)
return ssnField.hint
}, "xxx-xx-xxx")
}
How do do what is expressed in pseudo code work in kotlin?
You can return a reference of the property if you make createMask accept a parameter of type () -> KMutableProperty0<String>. Then you can call the set method:
fun createMask(mask : String, block: () -> KMutableProperty0<String>) {
block().set(mask)
}
// caller
createMask("xxx-xx-xxx") {
val ssnField = ...
ssnField::hint
}
Alternatively, use (String) -> Unit to represent "any function that takes a string", if you want to allow callers to pass any function that has the "form" of a setter.
fun createMask(mask : String, block: () -> (String) -> Unit) {
block()(mask)
}
// caller
createMask("xxx-xx-xxx") {
val ssnField = ...
ssnField::hint.setter
}
Note that this method involves reflection, which may not be desirable. Alternatively, you can accept a closure that takes the string to be set, and let the caller set it in the closure:
fun createMask(mask: String, block: (String) -> Unit) {
block(mask)
}
// caller
createMask("xxx-xx-xxx") {
val ssnField = ...
// note that rather than responsible for returning a property, the caller
// is responsible for setting "it" to the property
ssnField.hint = it
}
(I'm assuming createMask does more than just setting a property. Otherwise it is quite pointless...)

Get private method with reflection in order to pass it to a higher order function in Kotlin

I'm having a hard time trying to get a private method in Kotlin using reflection in order to pass it as a parameter to a higher order function, here is what I got and what I need to do:
The function that gets the private method, probably what I should change or fix:
inline fun <reified T> T.getPrivateFunc(name: String): KFunction<*> {
return T::class.declaredMemberFunctions.first {
it.name == name
}.apply {
isAccessible = true
}
}
This is the high order function I have:
class MyService {
fun myHigherOrderFunction(action: () -> Unit) { /*...*/ }
}
These are the class and the private method I need to get somehow:
class SystemUnderTest {
fun privateFunc() { /*...*/ }
}
Finally a unit test where I I'm trying to make sure the proper method is passed to the high order function, I omitted details for simplification:
// ...
val serviceMock = MyService()
val sut = SystemUnderTest()
// Here is what I'm trying to accomplish
val privateMethod = sut.getPrivateMethod("privateFunc")
service.myHighOrderFunction(privateMethod)
// In the above line I get a compilation error: required () - Unit, found KFunction<*>
service.myHigherOrderFunction(privateMethod as () -> Unit)
// In the above line I get the following runtime error:
// ClassCastException: kotlin.reflect.jvm.internal.KFunctionImpl cannot be cast to kotlin.jvm.functions.Function1
I know the test can be done having the privateFunc as public and maybe annotating it with #VisibleForTesting, but what I want is to avoid compromising the design as long as I can.
Any ideas? Thanks in advance!
I don't think KFunction and KCallable have any notion of a bound receiver, so they are not invokable (have no operator fun invoke), and therefore don't qualify as functions. So I think you have to wrap the KFunction object in a function to be able to pass it to your higher order function. To call a KFunction, you pass the instance of the receiver class as the first argument.
val serviceMock = MyService()
val sut = SystemUnderTest()
val privateMethod = sut.getPrivateMethod("privateFunc")
service.myHighOrderFunction { privateMethod.call(sut) }
Edit: To internalize the creation of the wrapped function, you could do this:
inline fun <reified T> T.getZeroArgPrivateMethod(name: String): () -> Unit = {
T::class.declaredMemberFunctions.first {
it.name == name
}.apply {
isAccessible = true
}.call(this)
}
//...
val serviceMock = MyService()
val sut = SystemUnderTest()
val privateMethod = sut.getZeroArgPrivateMethod("privateFunc")
service.myHighOrderFunction(privateMethod)

MockK - mock / spy top level extension method being called on top level val

In the MWE below I'm trying to verify that calling baz() also calls a method on another object. However, I can't seem to mock / spy on that object.
MWE:
package com.example
import io.mockk.every
import io.mockk.mockkStatic
import io.mockk.spyk
import io.mockk.verify
import org.junit.jupiter.api.Test
class FooBarTest {
#Test
fun `top level fun baz() calls theVal_bar()`() {
mockkStatic("com.example.FooBarTestKt")
val spy = spyk(theVal, name = "Hello, Spy!")
every { theVal } returns spy
// Should call bar() on the spy, but note that the spy's name is not printed
baz()
verify { spy.bar() }
}
}
class Foo
fun Foo.bar() = println("Foo.bar! name = $this")
val theVal = Foo()
fun baz() = theVal.bar()
This fails, because the call to theVal.bar() gets the val initialiser value instead of the mocked value spy.
How can I enforce the spy being used without changing the top level property definitions? In other words: I need a top level 'constant', but I want to mock it too. I could use val theVal get() = Foo(), which solves the issue, but it changes the code significantly, as it would replace the Foo instance every time.
Versions used:
- Kotlin 1.3.10
- MockK 1.8.13.kotlin13
- JUnit 5.3.1
The error:
java.lang.AssertionError: Verification failed: call 1 of 1: class com.example.FooBarTestKt.bar(eq(Foo(Hello, Spy!#1)))). Only one matching call to FooBarTestKt(static FooBarTestKt)/bar(Foo) happened, but arguments are not matching:
[0]: argument: com.example.Foo#476b0ae6, matcher: eq(Foo(Hello, Spy!#1)), result: -
Oh it is really madness when it comes to static and object mockks, and extension functions. To survive just think of extension functions as static functions with an argument.
Check, this is working because fooInstance is just an object passed as the first argument:
mockkStatic("kot.TestFileKt")
baz()
val fooInstance = theVal
verify { fooInstance.bar() }
Combining it doesn't work:
verify { theVal.bar() }
because it is as well verified.
This will also work(as I said Foo is just first argument to static method) as well:
mockkStatic("kot.TestFileKt")
baz()
verify { any<Foo>().bar() }
Instead of using an initialiser, use a backing (private) property and use get() for the val to be mocked:
private val _theVal = Foo()
val theVal get() = _theVal
Using a getter instead of initialiser creates a getter method without a static backing field. You can check the bytecode to see this:
Kotlin:
package com.example
#JvmField // See also: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields
val thisIsAField = "I'm static!"
val thisIsAValWithInitialiser = "I'm a static field too!"
val thisIsAValWithGetter get() = "I'm hardcoded in the getter method!"
Bytecode (I've removed much of the clutter so that my point becomes easier to see):
public final static Ljava/lang/String; thisIsAField
private final static Ljava/lang/String; thisIsAValWithInitialiser
public final static getThisIsAValWithInitialiser()Ljava/lang/String;
L0
LINENUMBER 6 L0
GETSTATIC com/example/FooBarTestKt.thisIsAValWithInitialiser : Ljava/lang/String;
ARETURN
L1
public final static getThisIsAValWithGetter()Ljava/lang/String;
L0
LINENUMBER 8 L0
LDC "I'm hardcoded in the getter method!"
ARETURN
L1
static <clinit>()V
L0
LINENUMBER 4 L0
LDC "I'm static!"
PUTSTATIC com/example/FooBarTestKt.thisIsAField : Ljava/lang/String;
L1
LINENUMBER 6 L1
LDC "I'm a static field too!"
PUTSTATIC com/example/FooBarTestKt.thisIsAValWithInitialiser : Ljava/lang/String;
RETURN
What can you see here? There is an important similarity between thisIsAField and thisIsAValWithInitialiser, being that they are backed by static fields. The getter method of thisIsAValWithInitialiser just returns that value. The value is private.
The similarity between thisIsAValWithInitialiser and thisIsAValWithGetter is that they are both public getter methods, but the difference is that the return value of thisIsAValWithGetter is hardcoded in the method body. This is simply a public method that MockK can override (even though it is final).
I guess (as I don't know the internals) that MockK cannot overrule GETSTATIC com/example/FooBarTestKt.thisIsAValWithInitialiser : Ljava/lang/String;, which is why a val initialiser cannot be mocked.