Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test code that calls top level functions in Kotlin?

I am very new to Kotlin.

I have a class that calls a top level function (which makes a http call). I am trying to write unit tests for my class without having it go out to the network.

Is there a way to mock/powermock/intercept the call from my class to the Kotlin top level function?

class MyClass {
    fun someMethod() {
        // do some stuff
        "http://somedomain.com/some-rest/action".httpGet(asList("someKey" to "someValue")).responseString { (request, response, result) ->
            // some processing code
        }
    }
}

It is using the kittinunf/Fuel library for the httpGet call.

It adds a top level function to String that ultimately calls a companion object function in Fuel (Fuel.get()).

The unit test needs to intercept the call to httpGet so that I can return a json string for the test.

like image 473
Boon Avatar asked Aug 01 '16 06:08

Boon


People also ask

What is top level function in Kotlin?

In addition to member functions and local functions, Kotlin also supports declaring top-level functions. These are functions that exist outside of any class, object, or interface and are defined directly inside a file.

How do I call a function in Kotlin?

Call a Function Now that you have created a function, you can execute it by calling it. To call a function in Kotlin, write the name of the function followed by two parantheses ().

How do you pass a list to a function in Kotlin?

Create a new List using the Kotlin standard library function listOf() , and pass in the elements of the list as arguments separated by commas. listOf(1, 2, 3, 4, 5, 6) returns a read-only list of integers from 1 through 6.


2 Answers

It seems that "top level functions" can be seen as static methods in disguise.

And from that point of view, the better answer is: do not use them in such a way. This leads to high, direct coupling; and makes your code harder to test. You definitely want to create some interface Service which all your objects should be using; and then use use dependency injection to equip your client code with some object that implements the Service interface.

By doing so, you also get rid of the requirement to Powermock completely.

like image 129
GhostCat Avatar answered Sep 22 '22 14:09

GhostCat


I encourage you to encapsulate remote API calls behind an interface that would be injected through constructor to the class using it:

class ResponseDto
interface SomeRest {
    fun action(data:Map<String,Any?>): ((ResponseDto)->Unit)->Unit
}
class FuelTests(val someRest: SomeRest) {
    fun callHttp(){
        someRest.action(mapOf("question" to "answer")).invoke { it:ResponseDto ->
            // do something with response 
        }
    }
}

Another way is to to inject a fake Client to be used by Fuel:

FuelManager.instance.client = object: Client {
    override fun executeRequest(request: Request): Response {
        return Response().apply {
            url = request.url
            httpStatusCode = 201
        }
    }
}

Fuel.testMode()

"http://somedomain.com/some-rest/action".httpGet(listOf()).responseString { request, response, result ->
    print(response.httpStatusCode) // prints 201
}
like image 41
miensol Avatar answered Sep 24 '22 14:09

miensol