-
Notifications
You must be signed in to change notification settings - Fork 292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Integration Testing with Ktor Client and Server in Kotlin Multiplatform #550
base: main
Are you sure you want to change the base?
Add Integration Testing with Ktor Client and Server in Kotlin Multiplatform #550
Conversation
Thanks for your contribution @kez-lab ! 🫶 |
val httpClient = HttpClient { | ||
install(ContentNegotiation) { | ||
json(Json { | ||
encodeDefaults = true | ||
isLenient = true | ||
coerceInputValues = true | ||
ignoreUnknownKeys = true | ||
}) | ||
} | ||
defaultRequest { | ||
host = "1.2.3.4" | ||
port = 8080 | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to introduce an extension function here instead. This way you can avoid the static instantiation and reuse the configuration in your test code.
Something like:
fun HttpClient.configuredForApi() = config {
install(ContentNegotiation) {
json(Json {
encodeDefaults = true
isLenient = true
coerceInputValues = true
ignoreUnknownKeys = true
})
}
defaultRequest {
host = "1.2.3.4"
port = 8080
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the suggestion! I've updated the code to use an extension function as you recommended. By introducing the configuredForApi extension function, the configuration logic is now reusable across both the main application code 😄
However, in the test code, I utilized testApplication, which does not require the use of the defaultRequest block. Therefore, I created a separate extension function specifically tailored for the test environment.
} | ||
|
||
android { | ||
namespace = "io.github.kez_lab.multipatform.full.integrationtest" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep a namespace similar to the one used in composeApp
module:
namespace = "io.github.kez_lab.multipatform.full.integrationtest" | |
namespace = "org.example.ktor.integrationtest" |
defaultConfig { | ||
minSdk = libs.versions.android.minSdk.get().toInt() | ||
} | ||
sourceSets["test"].java.srcDirs("src/test/kotlin") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modern versions of AGP add kotlin/
directory to source sets themself, so we can drop this line
ktor-server-test-host = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" } | ||
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } | ||
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } | ||
ktor-server-content-negotiation-jvm = { module = "io.ktor:ktor-server-content-negotiation-jvm", version.ref = "ktor" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-jvm
suffix could be dropped when we use Gradle as a build system:
ktor-server-content-negotiation-jvm = { module = "io.ktor:ktor-server-content-negotiation-jvm", version.ref = "ktor" } | |
ktor-server-content-negotiation= { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" } |
import io.ktor.serialization.kotlinx.json.json | ||
import kotlinx.serialization.json.Json | ||
|
||
fun HttpClient.configuredForApi() = config { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function could both, create and configure an HttpClient, since you don't reuse this extension from tests:
fun HttpClient.configuredForApi() = config { | |
fun createHttpClient() = HttpClient { |
In real life this method would be called in some DI framework providing a singleton HttpClient.
@@ -0,0 +1,42 @@ | |||
import core.extension.createConfiguredClient |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a package to tests. You don't have to put the files into physical packages, just add a correct package
directive on the top of each file.
Hello,
I’ve been delayed in submitting this Pull Request due to other pressing tasks. However, I am now submitting a PR inspired by the excellent advice from the Ktor team regarding integration testing.
The current test cases are kept at a basic level for simplicity, but I am more than willing to add more detailed tests if necessary.
I appreciate your time and consideration in reviewing this PR and welcome any feedback you might have.
Thank you!
Description
This PR provides a guide on performing integration testing in Kotlin Multiplatform (KMP) projects using Ktor. The guide includes:
This pull request focuses on enhancing test coverage, ensuring system stability, and reducing unexpected issues in production environments.
Changes
Testing
Additional Information
This guide proposes methods to go beyond unit testing by verifying system-wide interactions, thereby enhancing test coverage and building a more reliable application.
References