Aharon Levine
Aharon Levine

Reputation: 121

Testing Post requests in Ktor

Ktor (kotlin web framework) has an awesome testable mode where http requests can be wrapped in unit tests. They give a nice example of how to test a GET endpoint here, however I'm having trouble with an http POST.

I tried this but the post params don't seem to be added to the request:

    @Test
fun testSomePostThing() = withTestApplication(Application::myModule) {
    with(handleRequest(HttpMethod.Post, "/api/v2/processing") {
        addHeader("content-type", "application/x-www-form-urlencoded")
        addHeader("Accept", "application/json")
        body = "param1=cool7&param2=awesome4"
    }) {
        assertEquals(HttpStatusCode.OK, response.status())
        val resp = mapper.readValue<TriggerResponse>(response.content ?: "")
        assertEquals(TriggerResponse("cool7", "awesome4", true), resp)
    }
}

Anyone have any ideas?

Upvotes: 5

Views: 5993

Answers (4)

jpthesolver2
jpthesolver2

Reputation: 1155

For those using the alternate .apply to verify results, you can prepend the body before the test call

withTestApplication({ module(testing = true) }) {
            handleRequest(HttpMethod.Post, "/"){
                setBody(...)
            }.apply {
                assertEquals(HttpStatusCode.OK, response.status())
                assertEquals("HELLO WORLD!", response.content)
            }
        }

Upvotes: 3

Alexey Soshin
Alexey Soshin

Reputation: 17691

For those reading it nowadays, back in 2018 receiveParameters() method was added for such cases. You can use it as:

install(Routing) {
        post("/api/v2/processing") {
            val params = call.receiveParameters()
            println(params["param1"]) // Prints cool7
            ...
        }
}

Also it's worth noting that request construction in the example could be further improved nowadays:

// Use provided consts, not strings
addHeader(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded.toString())
// Convenient method instead of constructing string requests 
setBody(listOf("param1" to "cool7", "param2" to "awesome4").formUrlEncode())

Upvotes: 2

avolkmann
avolkmann

Reputation: 3105

call.parameters also works for post routes.

get("api/{country}") {
    val country = call.parameters["country"]!!
    ...
}

This will give you whatever is passed in the uri after api.

call.receive is for the body of a request.

Upvotes: 1

Aharon Levine
Aharon Levine

Reputation: 121

Ok dumb mistake, I'll post it here in case this saves somebody else from wasting time ;) The unit test was actually catching a real problem (thats what they're for I guess) In my routing I was using:

install(Routing) {
        post("/api/v2/processing") {
            val params = call.parameters
            ...
        }
}

However that only works for 'get' params. Post params need:

install(Routing) {
        post("/api/v2/processing") {
            val params = call.receive<ValuesMap>()
            ...
        }
}

Upvotes: 2

Related Questions