Kotest
멀티플랫폼인 KMP에서 테스트코드를 작성하기 위해서는 Kotlin에서 제공하는 테스트 프레임워크인 Kotest를 사용해야 한다.
Kotest에는 종류가 여러 가지가 있는데 나는 BehaviorSpec을 선택했다.
그 이유는 BehaviorSpec은 아래처럼 given - when - then 형식으로 표현이 가능하여 더 체계적으로 작성할 수 있겠다고 생각했기 때문이다.
class UiStateTest : BehaviorSpec({
given("테스트 조건 환경이 주어졌을 때") {
...
`when`("테스트 대상 행위가 발생하면") {
...
then("의도한 결과가 검증될 것이다.") {
...
}
}
}
})
적용하기
본격적으로 Kotest를 적용하는 방법에 대해 설명하겠다.
우선, Settings > Plugins에서 Kotest를 다운로드 받아야 한다.
(필자는 이를 다운받지 않아 테스트 코드 실행버튼이 뜨질 않았다.)
그다음 필요한 라이브러리를 넣어준다.
아래 공식문서를 참고하면 좋다.
Quick Start | Kotest
Kotest is divided into several, stand alone, subprojects, each of which can be used independently:
kotest.io
단순히 안드로이드에서만 사용하는 거라면 아래처럼 추가해주면 된다.
dependencies {
testImplementation 'io.kotest:kotest-runner-junit5:$version'
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
그러나 나는 KMP에 적용해줘야 하기 때문에 commonTest를 활용해야 한다.
테스트 코드는 별도의 commonTest 모듈에서 작성하기 때문에 우리에게 익숙한 testImplementation과 똑같은 역할을 하는 commonTest.dependencies 블록 안에 implementation으로 라이브러리를 넣어주는 것이다.
[versions]
kotest = "5.9.0"
[libraries]
kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
kotest-framework-engine = { module = "io.kotest:kotest-framework-engine", version.ref = "kotest" }
kotest-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }
[plugins]
kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotest" }
plugins {
alias(libs.plugins.kotest.multiplatform) // (1)
}
kotlin {
sourceSets {
commonMain.dependencies {
...
}
commonTest.dependencies { // (2)
implementation(libs.kotest.assertions.core)
implementation(libs.kotest.framework.engine)
}
androidUnitTest.dependencies { // (3)
implementation(libs.kotest.runner.junit5)
}
}
}
tasks.withType<Test> { // (4)
useJUnitPlatform()
}
(1) kotest를 활용할 수 있게 플러그인을 넣어준다.
(2) commonTest 모듈에서 kotest를 사용하기 위한 프레임워크를 추가한다.
(3) 안드로이드에서 로컬 유닛 테스트를 돌릴 때 쓰는 테스트 엔진을 넣어준다. (안 그러면 로컬에서 테스트 돌릴 때 에러가 난다.)
(4) Gradle 테스트 태스크가 JUnit 플랫폼 방식을 사용해 Kotest 스펙을 인식하게 한다.
마지막으로 테스트 코드를 작성할 모듈을 commonTest로 추가 해서 코드를 작성하면 된다.
테스트 코드
내가 테스트 해볼 코드는 Sponsor 리스트가 주어졌을 때 내가 원하는 결과로 정렬이 되는지 보는 것이었다.
KMP로 마이그레이션 하기 전에는 Java의 TreeMap 구조에 의존하는 toSortedMap()을 사용하고 있었기 때문에 로직을 바꿔줘야만 했다.
class SponsorUiStateTest : BehaviorSpec({
given("다음과 같은 Sponsor가 주어졌을 때") {
val sponsors = listOf(
Sponsor(
name = "당근",
imageUrl = "https://raw.githubusercontent.com/droidknights/DroidKnightsApp/main/feature/home/src/main/res/drawable/img_sponsor_daangn.png",
homepage = "https://about.daangn.com/",
grade = Sponsor.Grade.PLATINUM
),
// 생략
`when`("SponsorsUiState.Sponsors 이면") {
val state = SponsorsUiState.Sponsors(sponsors)
then("Grade의 priority 별로 정렬이 될 것이다") {
val grouped = state.groupedSponsorsByGrade
grouped.size shouldBe 3 // (1)
grouped[0].map { it.name } shouldContainExactly listOf("당근", "카카오뱅크") // (2)
grouped[1].map { it.name } shouldContainExactly listOf("젯브레인", "점핏")
grouped[2].map { it.name } shouldContainExactly listOf("레진 엔터테인먼트", "헤이딜러")
}
}
}
})
then 문을 보면,
(1) shouldBe를 사용해 객체 비교를 직관적으로 해주고 있다. group으로 묶었을 시, 총 3가지 종류밖에 없기 때문이다.
(2) shouldContainExactly를 사용해 컬렉션의 내용과 순서를 정확히 비교하고 있다.
테스트 결과는 성공이다!
마치며
최근 테스트 공부를 하고 있었기 때문에 kotest로도 멀티플랫폼에 적용해 본 경험은 좋았던 것 같다.
앞으로 리팩토링을 해야 할 때나 복잡한 로직이 있으면 테스트 코드를 적극적으로 활용해보고 싶다. (테스트코드가 익숙해질때까지...🥹)
이 모든 것을 적용한 과정은 아래 PR로 확인바란다!
https://github.com/Kotlin-Multiplatform-Laboratory/DroidKnightsApp-KMP/pull/19
[feature/home] Home 모듈 CMP 마이그레이션 by leeeyubin · Pull Request #19 · Kotlin-Multiplatform-Laboratory/DroidKnigh
Overview (Required) Home 모듈 CMP로 마이그레이션 했습니다. Kotest로 테스트코드도 작성했습니다. Screenshot
github.com
'Develop > KMP' 카테고리의 다른 글
[KMP] Kotest로 알아본 Stub vs Fake (테스트 더블) (0) | 2025.03.22 |
---|