Ktor testing Fail to serialize body. Content has type: class ... but OutgoingContent expected
Asked Answered
D

2

6

so trying to understand how I can make testing ktor app with testcontainers

this is my code

package com.app

import com.app.base.db.DbFactory
import com.app.features.auth.RegisterDTO
import com.app.plugins.*
import io.kotest.assertions.ktor.client.shouldHaveStatus
import io.kotest.core.extensions.install
import io.kotest.core.spec.style.FreeSpec
import io.kotest.extensions.testcontainers.JdbcTestContainerExtension
import io.ktor.client.*
import io.ktor.client.engine.apache.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import org.testcontainers.containers.PostgreSQLContainer
import io.ktor.server.testing.*
import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.Database

class AuthSpec : FreeSpec({

    val postgres = PostgreSQLContainer<Nothing>("postgres").apply {
        withDatabaseName("test_appDB")
        startupAttempts = 1
        withUsername("test_viktor")
        withPassword("test_longPass")
        withExposedPorts(5432)
    }
    postgres.start()

    val ds = install(JdbcTestContainerExtension(postgres)) {
        poolName = "myconnectionpool"
        maximumPoolSize = 8
        idleTimeout = 10000
    }

    "register creator" - {
        testApplication {
            application {
                DbFactory.init(ds)
                configureSecurity()
                configureHTTP()
                configureMonitoring()
                configureSerialization()
                configureDependencyInjection()
                configureStatusPage()
                configureRouting()
            }

            val client = HttpClient(Apache) {}
            client.post("/api/v1/auth/register") {
                contentType(ContentType.Application.Json)
                setBody(RegisterDTO("[email protected]", "some", "some@sdfSDF"))
            }.apply {
                this.shouldHaveStatus(HttpStatusCode.OK)
            }
        }
    }

})

I do expect that this test will run correctly and I can see the green in tests but actually i see the error

Fail to serialize body. Content has type: class com.app.features.auth.RegisterDTO, but OutgoingContent expected.
java.lang.IllegalStateException: Fail to serialize body. Content has type: class com.app.features.auth.RegisterDTO, but OutgoingContent expected.
If you expect serialized body, please check that you have installed the corresponding plugin(like `ContentNegotiation`) and set `Content-Type` header.
    at io.ktor.client.plugins.HttpSend$Plugin$install$1.invokeSuspend(HttpSend.kt:87)
    at io.ktor.client.plugins.HttpSend$Plugin$install$1.invoke(HttpSend.kt)
    at io.ktor.client.plugins.HttpSend$Plugin$install$1.invoke(HttpSend.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(SuspendFunctionGun.kt:91)
    at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invokeSuspend(HttpCallValidator.kt:126)
    at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invoke(HttpCallValidator.kt)
    at io.ktor.client.plugins.HttpCallValidator$Companion$install$1.invoke(HttpCallValidator.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
    at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invokeSuspend(HttpRequestLifecycle.kt:35)
    at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invoke(HttpRequestLifecycle.kt)
    at io.ktor.client.plugins.HttpRequestLifecycle$Plugin$install$1.invoke(HttpRequestLifecycle.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute$ktor_utils(SuspendFunctionGun.kt:101)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:77)
    at io.ktor.client.HttpClient.execute$ktor_client_core(HttpClient.kt:187)
    at io.ktor.client.statement.HttpStatement.executeUnsafe(HttpStatement.kt:107)
    at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:46)
    at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:61)
    at com.app.AuthSpec$1$1$1.invokeSuspend(AuthSpec.kt:84)

i have installed a lot of libraries for tests probably they are not needed but here are they in dependendancies

   //region tests
    testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
    testImplementation("io.ktor:ktor-server-test-host:$ktor_version")
    testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlin_version")

    testImplementation("io.kotest:kotest-runner-junit5:$kotest_version")
    testImplementation("io.kotest:kotest-assertions-core:$kotest_version")
    testImplementation("io.kotest:kotest-property:$kotest_version")
    testImplementation("io.kotest.extensions:kotest-extensions-testcontainers:1.3.2")
    testImplementation("org.testcontainers:testcontainers:$testcontainers_version")
    testImplementation("org.testcontainers:junit-jupiter:$testcontainers_version")
    testImplementation("org.testcontainers:postgresql:$testcontainers_version")
    testImplementation("io.kotest.extensions:kotest-assertions-ktor:1.0.3")
    //endregion
Drugget answered 6/6, 2022 at 16:8 Comment(0)
E
5

You need to install the ContentNegotiation plugin by configuring the test client.

Embolus answered 6/6, 2022 at 16:28 Comment(0)
Y
2

Add following to build.gradle.kts (in this case serialization.kotlinx.json , but you can use Gson or Jackson):

implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")

And install to the client config:

val client = HttpClient(Apache) {
    install(ContentNegotiation) {
        json()
    }
}
Yerkovich answered 6/5 at 11:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.