π― Assertion
π What is an Assertion?β
Assertion is verifying the expected results in tests. With Kotest, you can perform more readable and powerful validations!
π‘ Kotest Setupβ
build.gradle.ktsβ
dependencies {
testImplementation("io.kotest:kotest-runner-junit5:5.5.0")
testImplementation("io.kotest:kotest-assertions-core:5.5.0")
}
π― Basic Assertionsβ
shouldBeβ
import io.kotest.matchers.shouldBe
import io.kotest.core.spec.style.StringSpec
class BasicAssertionTest : StringSpec({
"μ«μ λΉκ΅" {
val result = 2 + 3
result shouldBe 5
}
"λ¬Έμμ΄ λΉκ΅" {
val name = "Kotlin"
name shouldBe "Kotlin"
}
"λΆλ¦° κ²μ¦" {
val isValid = true
isValid shouldBe true
}
})
Various Matchersβ
import io.kotest.matchers.*
import io.kotest.matchers.string.*
class MatcherTest : StringSpec({
"λ€μν λΉκ΅" {
// λλ±μ±
5 shouldBe 5
5 shouldNotBe 6
// ν¬κΈ° λΉκ΅
10 shouldBeGreaterThan 5
3 shouldBeLessThan 10
5 shouldBeInRange 1..10
// λ¬Έμμ΄
"hello" shouldStartWith "he"
"world" shouldEndWith "ld"
"kotlin" shouldContain "otl"
"test" shouldHaveLength 4
}
})
π¨ Collection Assertionsβ
List Validationβ
import io.kotest.matchers.collections.*
class CollectionTest : StringSpec({
"리μ€νΈ κ²μ¦" {
val numbers = listOf(1, 2, 3, 4, 5)
// ν¬κΈ°
numbers shouldHaveSize 5
numbers.shouldNotBeEmpty()
// ν¬ν¨
numbers shouldContain 3
numbers shouldContainAll listOf(1, 3, 5)
// μμ
numbers.shouldBeSorted()
// 쑰건
numbers.forAll { it shouldBeGreaterThan 0 }
numbers.forAtLeastOne { it shouldBe 3 }
}
})
Map Validationβ
class MapTest : StringSpec({
"Map κ²μ¦" {
val map = mapOf("name" to "νκΈΈλ", "age" to "25")
// ν€/κ° μ‘΄μ¬
map shouldContainKey "name"
map shouldContainValue "νκΈΈλ"
// νλͺ© κ²μ¦
map shouldContain ("name" to "νκΈΈλ")
// ν¬κΈ°
map shouldHaveSize 2
}
})
π₯ Practical Examplesβ
User Validationβ
data class User(
val name: String,
val email: String,
val age: Int
)
class UserTest : StringSpec({
"μ¬μ©μ μ 보 κ²μ¦" {
val user = User("νκΈΈλ", "hong@example.com", 25)
user.name shouldBe "νκΈΈλ"
user.email shouldContain "@"
user.age shouldBeInRange 1..100
}
"μ΄λ©μΌ νμ κ²μ¦" {
val user = User("κΉμ² μ", "kim@test.com", 30)
user.email shouldStartWith "kim"
user.email shouldEndWith ".com"
user.email shouldMatch Regex("\\w+@\\w+\\.\\w+")
}
})
Order Validationβ
data class Order(
val id: String,
val items: List<Item>,
val total: Double
)
data class Item(val name: String, val price: Double)
class OrderTest : StringSpec({
"μ£Όλ¬Έ κ²μ¦" {
val order = Order(
id = "ORDER-001",
items = listOf(
Item("μ¬κ³Ό", 1000.0),
Item("λ°λλ", 1500.0)
),
total = 2500.0
)
// ID κ²μ¦
order.id shouldStartWith "ORDER-"
// μμ΄ν
κ²μ¦
order.items shouldHaveSize 2
order.items.forAll { it.price shouldBeGreaterThan 0.0 }
// ν©κ³ κ²μ¦
order.total shouldBe 2500.0
}
})
π‘οΈ Exception Assertionsβ
Validating Exceptionsβ
import io.kotest.assertions.throwables.*
class ExceptionTest : StringSpec({
"μμΈ λ°μ κ²μ¦" {
shouldThrow<IllegalArgumentException> {
validateAge(-1)
}
}
"μμΈ λ©μμ§ κ²μ¦" {
val exception = shouldThrow<IllegalArgumentException> {
validateAge(-1)
}
exception.message shouldBe "λμ΄λ 0 μ΄μμ΄μ΄μΌ ν©λλ€"
}
"μμΈ λ°μ μν¨" {
shouldNotThrowAny {
validateAge(25)
}
}
})
fun validateAge(age: Int) {
require(age >= 0) { "λμ΄λ 0 μ΄μμ΄μ΄μΌ ν©λλ€" }
}
π― Soft Assertionsβ
Multiple Validations at Onceβ
import io.kotest.assertions.assertSoftly
class SoftAssertionTest : StringSpec({
"Soft Assertion" {
val user = User("νκΈΈλ", "hong@example.com", 25)
assertSoftly(user) {
name shouldBe "νκΈΈλ"
email shouldContain "@"
age shouldBeInRange 20..30
}
}
"λͺ¨λ μ€ν¨λ₯Ό νλ²μ 보기" {
assertSoftly {
1 shouldBe 2 // μ€ν¨ν΄λ κ³μ
"hello" shouldBe "world" // μ΄κ²λ 체ν¬
true shouldBe false // μ΄κ²λ 체ν¬
}
// λͺ¨λ μ€ν¨λ₯Ό νλ²μ 보μ¬μ€
}
})
π§ Custom Matchersβ
Your Own Validationβ
import io.kotest.matchers.Matcher
import io.kotest.matchers.MatcherResult
import io.kotest.matchers.should
fun beEven() = Matcher<Int> { value ->
MatcherResult(
value % 2 == 0,
{ "$valueλ μ§μμ¬μΌ ν©λλ€" },
{ "$valueλ μ§μκ° μλμ΄μΌ ν©λλ€" }
)
}
fun bePositive() = Matcher<Int> { value ->
MatcherResult(
value > 0,
{ "$valueλ μμμ¬μΌ ν©λλ€" },
{ "$valueλ μμκ° μλμ΄μΌ ν©λλ€" }
)
}
class CustomMatcherTest : StringSpec({
"컀μ€ν
Matcher" {
4 should beEven()
10 should bePositive()
}
})
π¨ Data Class Comparisonβ
Partial Comparisonβ
data class Person(
val name: String,
val age: Int,
val address: String
)
class PersonTest : StringSpec({
"λ°μ΄ν° ν΄λμ€ λΉκ΅" {
val person = Person("νκΈΈλ", 25, "μμΈ")
// μ 체 λΉκ΅
person shouldBe Person("νκΈΈλ", 25, "μμΈ")
// λΆλΆ λΉκ΅
person.name shouldBe "νκΈΈλ"
person.age shouldBeInRange 20..30
}
})
π₯ Practical Patternsβ
Range Validationβ
class RangeTest : StringSpec({
"λ²μ κ²μ¦" {
val score = 85
score shouldBeInRange 0..100
score shouldBeGreaterThan 60
score shouldBeLessThan 90
}
"λ μ§ λ²μ" {
val today = "2024-12-25"
today shouldBeGreaterThan "2024-01-01"
today shouldBeLessThan "2025-01-01"
}
})
Type Validationβ
import io.kotest.matchers.types.*
class TypeTest : StringSpec({
"νμ
κ²μ¦" {
val value: Any = "hello"
value.shouldBeInstanceOf<String>()
value shouldBe instanceOf<String>()
}
"Null κ²μ¦" {
val nullValue: String? = null
val nonNull: String? = "test"
nullValue.shouldBeNull()
nonNull.shouldNotBeNull()
}
})
Regex Validationβ
class RegexTest : StringSpec({
"μ κ·μ λ§€μΉ" {
val email = "hong@example.com"
email shouldMatch Regex("\\w+@\\w+\\.\\w+")
}
"μ νλ²νΈ κ²μ¦" {
val phone = "010-1234-5678"
phone shouldMatch Regex("\\d{3}-\\d{4}-\\d{4}")
}
})
π€ Frequently Asked Questionsβ
Q1. shouldBe vs assertEquals?β
A: shouldBe is more readable!
// JUnit
assertEquals(5, result)
// Kotest
result shouldBe 5 // λ μμ°μ€λ¬μ
Q2. Customizing failure messages?β
A: Use withClue!
import io.kotest.assertions.withClue
class ClueTest : StringSpec({
"컀μ€ν
λ©μμ§" {
withClue("μ¬μ©μ λμ΄ κ²μ¦ μ€ν¨") {
25 shouldBeInRange 20..30
}
}
})
Q3. Only one of multiple conditions?β
A: Use anyOf!
import io.kotest.matchers.or
class AnyOfTest : StringSpec({
"μ¬λ¬ 쑰건 μ€ νλ" {
val value = 5
value shouldBe (3 or 5 or 7)
}
})
π¬ Conclusionβ
Reliable validation with powerful assertions!
Key Takeaways:
β
Concise validation with shouldBe
β
Utilize various matchers
β
All at once with Soft Assertions
β
Write custom matchers
β
Type/Range/Regex validation
Next Step: Learn about test doubles in Mocking!