🎯 Assertion
📖 什么是Assertion?
Assertion是在测试中验证预期结果。使用Kotest,您可以执行更易读、更强大的验证!
💡 Kotest配置
build.gradle.kts
dependencies {
testImplementation("io.kotest:kotest-runner-junit5:5.5.0")
testImplementation("io.kotest:kotest-assertions-core:5.5.0")
}
🎯 基本Assertion
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
}
})
各种Matcher
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
}
})
🎨 集合Assertion
列表验证
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验证
class MapTest : StringSpec({
"Map 검증" {
val map = mapOf("name" to "홍길동", "age" to "25")
// 키/값 존재
map shouldContainKey "name"
map shouldContainValue "홍길동"
// 항목 검증
map shouldContain ("name" to "홍길동")
// 크기
map shouldHaveSize 2
}
})
🔥 实战示例
用户验证
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+")
}
})
订单验证
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
}
})
🛡️ 异常Assertion
验证异常
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 Assertion
一次多个验证
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 // 이것도 체크
}
// 모든 실패를 한번에 보여줌
}
})
🔧 自定义Matcher
您自己的验证
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 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
}
})
🔥 实用模式
范围验证
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"
}
})
类型验证
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()
}
})
正则表达式验证
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}")
}
})
🤔 常见问题
Q1. shouldBe vs assertEquals?
A: shouldBe更易读!
// JUnit
assertEquals(5, result)
// Kotest
result shouldBe 5 // 더 자연스러움
Q2. 自定义失败消息?
A: 使用withClue!
import io.kotest.assertions.withClue
class ClueTest : StringSpec({
"커스텀 메시지" {
withClue("사용자 나이 검증 실패") {
25 shouldBeInRange 20..30
}
}
})
Q3. 多个条件中的一个?
A: 使用anyOf!
import io.kotest.matchers.or
class AnyOfTest : StringSpec({
"여러 조건 중 하나" {
val value = 5
value shouldBe (3 or 5 or 7)
}
})
🎬 总结
使用强大的Assertion进行可靠验证!
核心要点:
✅ 使用shouldBe进行简洁验证
✅ 利用各种Matcher
✅ 使用Soft Assertion一次性完成
✅ 编写自定义Matcher
✅ 类型/范围/正则表达式验证
下一步: 在Mocking中了解测试替身!