I am building a server using the Ktor framework in Kotlin. I am currently writing a test for my API route that registers a user, but I keep encountering an error when I attempt to send a JSON body to the server. The error message indicates that serialization fails when trying to serialize the class to the HTTP body.
I followed the Ktor testing documentation and learned that I need to install ContentNegotiation in my test client. However, despite doing this, the error persists.
Here is my current code:
@Serializable
data class User(val email: String, val password: String)
fun ApplicationTestBuilder.createEnvironment() {
val fakeUserRepository = FakeUserRepository()
val jwtUserService = FakeJWTUserService(fakeUserRepository)
val fakeUserService = FakeUserService(fakeUserRepository, jwtUserService)
application {
configureSerialization()
configureRouting(fakeUserService)
}
}
@Test
fun testRegister() = testApplication {
createEnvironment()
val client = createClient {
install(ContentNegotiation) {
json()
}
}
val validUser = User(
"[email protected]",
"Simone_2006",
)
client.post("users/register") {
contentType(ContentType.Application.Json)
setBody(validUser)
}.apply {
assertEquals(HttpStatusCode.OK, status)
}
}
However, the code fails to work as expected. Here is the error message:
fun <P : Pipeline<*, ApplicationCall>, B : Any, F : Any> install(plugin: Plugin<P, B, F>, configure: B.() -> Unit = ...): Unit' cannot be called in this context with an implicit receiver. Use an explicit receiver if necessary.
I also tried moving the install(ContentNegotiation) block outside the createClient function, as shown below:
fun ApplicationTestBuilder.createEnvironment() {
val fakeUserRepository = FakeUserRepository()
val jwtUserService = FakeJWTUserService(fakeUserRepository)
val fakeUserService = FakeUserService(fakeUserRepository, jwtUserService)
application {
configureSerialization()
configureRouting(fakeUserService)
}
}
@Test
fun testRegister() = testApplication {
createEnvironment()
install(ContentNegotiation) {
json()
}
val validUser = User(
"[email protected]",
"Simone_2006",
)
client.post("users/register") {
contentType(ContentType.Application.Json)
setBody(validUser)
}.apply {
assertEquals(HttpStatusCode.OK, status)
}
}
Despite trying both approaches, I still get an error message:
Fail to prepare request body for sending.
The body type is: class com.example.user.model.User, with Content-Type: application/json.
If you expect serialized body, please check that you have installed the corresponding plugin(like `ContentNegotiation`) and set `Content-Type` header.
java.lang.IllegalStateException: Fail to prepare request body for sending.
The body type is: class com.example.user.model.User, with Content-Type: application/json.
If you expect serialized body, please check that you have installed the corresponding plugin(like ContentNegotiation
) and set Content-Type
header.
and the JSON serialization doesn't seem to work. I also verified that the configureSerialization() method is correctly applied to my application, but I'm unsure if the issue lies there.
Here is whole code:
package com.example
import com.example.plugins.configureRouting
import com.example.plugins.configureSerialization
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.testing.*
import kotlin.test.*
data class User(val email: String, val password: String)
class ApplicationTest {
fun ApplicationTestBuilder.createEnvironment() {
application {
configureRouting()
configureSerialization()
}
}
@Test
fun testRegister() = testApplication {
createEnvironment()
val client = createClient {
install(ContentNegotiation) {
json()
}
}
val validUser = User(
"[email protected]",
"Simone_2006",
)
client.post("/users/register") {
contentType(ContentType.Application.Json)
setBody(validUser)
}
}
}
I couldn't find similar issues online, so I would appreciate any help in resolving this.
The problem is that the server ContentNegotiation
plugin is installed into the test client because you have the following import:
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
To solve the problem, you need to import the client ContentNegotiation
plugin:
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation