반응형
dependencies
dependencies {
...
testImplementation("io.kotest:kotest-runner-junit5:5.7.0")
testImplementation("io.kotest:kotest-property:5.7.0")
testImplementation("io.kotest:kotest-assertions-core:5.7.0")
testImplementation("io.kotest:kotest-extensions-now:5.7.0")
}
tasks.withType<Test> {
useJUnitPlatform()
}
basic test
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
class DemoTest : FunSpec({
context("demo") {
test("1 == 1") {
"1" shouldBe "1"
}
}
})
exception test
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.should
import io.kotest.matchers.string.startWith
class MyTests : StringSpec({
"test" {
val exception = shouldThrow<IllegalStateException> {
throw IllegalStateException("test exception")
}
exception.message should startWith("test")
}
})
ordering test
import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.TestCaseOrder
class MyTests : StringSpec() {
// TestCaseOrder.Sequential : 정의된 순서에 맞춰서 실행. 디폴트
// TestCaseOrder.Random : 랜덤 순서로 실행.
// TestCaseOrder.Lexicographic : 사전 순으로 실행.
override fun testCaseOrder(): TestCaseOrder = TestCaseOrder.Sequential
init {
"foo" {
// I run first as I'm defined first
}
"bar" {
// I run second as I'm defined second
}
}
}
resource test
import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.StringSpec
import java.io.StringReader
class MyTests : StringSpec({
isolationMode = IsolationMode.InstancePerLeaf
val reader = autoClose(StringReader("xyz"))
"test" {
println(reader.readText())
}
"test2" {
println(reader.readText())
}
})
temporary files example
import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.StringSpec
import io.kotest.engine.spec.tempdir
import io.kotest.engine.spec.tempfile
class MyTests : StringSpec({
isolationMode = IsolationMode.InstancePerLeaf
val file = tempfile()
val dir = tempdir()
"test" {
println(file.name)
println(dir.name)
}
"test2" {
println(file.name)
println(dir.name)
}
})
ConstantNowTestListener
import io.kotest.core.spec.style.FunSpec
import io.kotest.extensions.time.withConstantNow
import io.kotest.matchers.shouldBe
import java.time.LocalDateTime
class ConstantNowTest : FunSpec({
test("test") {
val foreverNow = LocalDateTime.now()
withConstantNow(foreverNow) {
LocalDateTime.now() shouldBe foreverNow
}
}
})
import io.kotest.core.spec.style.FunSpec
import io.kotest.extensions.time.ConstantNowTestListener
import io.kotest.matchers.shouldBe
import java.time.LocalDateTime
class ConstantNowTest : FunSpec({
val now = LocalDateTime.now()
listeners(ConstantNowTestListener(now))
test("test") {
LocalDateTime.now() shouldBe now
}
})
ConstantLocalDateNowTestListener
- 위 방식으로 LocalDateTime.now()는 mocking할 수 있지만 LocalDate.now()는 할 수 없다.
- 따라서 LocalDate.now() mocking을 위해 직접 Listener 구현 후 사용.
class ConstantLocalDateNowTestListener(private val now: LocalDate) : TestListener {
override suspend fun beforeAny(testCase: TestCase) {
mockkStatic(LocalDate::class)
every { LocalDate.now() } returns now
}
override suspend fun afterAny(testCase: TestCase, result: TestResult) {
unmockkStatic(LocalDate::class)
}
}
class ConstantNowTest : FunSpec({
isolationMode = IsolationMode.InstancePerLeaf
val now = LocalDate.parse("20240101", DateTimeFormatter.ofPattern("yyyyMMdd"))
listeners(ConstantLocalDateNowTestListener(now))
test("test") {
LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) shouldBe "20240101"
}
})
fail fast test
import io.kotest.core.spec.style.FunSpec
class FailFastTests : FunSpec({
// failfast = true : 컨텍스트 하위의 테스트 중 하나가 실패하면 그 이후 실행될 테스트는 skip
context("context with fail fast enabled").config(failfast = true) {
test("a") {} // pass
test("b") { error("boom") } // fail
test("c") {} // skipped
context("d") { // skipped
test("e") {} // skipped
}
}
})
spec level setting
import io.kotest.core.spec.style.FunSpec
class FailFastTests : FunSpec({
failfast = true
context("context with fail fast enabled") {
test("a") {} // pass
test("b") { error("boom") } // fail
test("c") {} // skipped
context("d") { // skipped
test("e") {} // skipped
}
}
})
KotestProjectConfig
예제 코드
class DemoTest1 : FunSpec({
val uuid = UUID.randomUUID().toString()
test("test1-a") {
Thread.sleep(1000)
println("test1-a-$uuid")
}
test("test1-b") {
Thread.sleep(1000)
println("test1-b-$uuid")
}
})
class DemoTest2 : FunSpec({
val uuid = UUID.randomUUID().toString()
test("test2-a") {
Thread.sleep(1000)
println("test2-a-$uuid")
}
test("test2-b") {
Thread.sleep(1000)
println("test2-b-$uuid")
}
})
KotestProjectConfig 설정 전
# 같은 클래스의 uuid가 동일하고 4초동안 실행
# 기본 isolationMode가 SPEC이고, 한 쓰레드에서 테스트가 실행되기 때문
test1-a-f5b4c250-21cf-47a5-b306-36c0058968d5
test1-b-f5b4c250-21cf-47a5-b306-36c0058968d5
test2-a-90fff1c8-b0e5-4654-9eaa-6cdae41e5a98
test2-b-90fff1c8-b0e5-4654-9eaa-6cdae41e5a98
KotestProjectConfig 설정 후
class KotestProjectConfig : AbstractProjectConfig() {
override val isolationMode = IsolationMode.InstancePerLeaf // 테스트 모드 글로벌 설정
override val parallelism = 2 // 테스트 병렬 처리 수
}
# 같은 클래스여도 uuid가 다르고 2초동안 실행
# 글로벌 설정으로 isolationMode는 LEAF이고, 쓰레드 2개에서 테스트가 실행되기 때문. (테스트별로 쓰레드 하나씩 할당되는듯)
test2-a-11f0fdbb-9698-4f8d-b535-ab4f433fe1eb
test1-a-ab4f9392-e72f-4432-a985-4985dc8db12e
test2-b-9a4eb3b9-88bc-4490-af32-b63da96b6ee0
test1-b-cdbec987-9cd8-439c-b763-d693dabf2e71
반응형
'Development > Kotest' 카테고리의 다른 글
[Kotest] extensions (0) | 2023.11.04 |
---|---|
[Kotest] lifecycle hook (0) | 2023.11.04 |
[Kotest] isolation modes (0) | 2023.11.04 |
[Kotest] conditional evaluation (0) | 2023.11.04 |
[Kotest] testing styles (0) | 2023.11.04 |