프로그래밍/Kotlin

Kotest 기본만 알아보자

seungdols 2023. 4. 17. 16:34

kotest

link: https://kotest.io

Kotest is a flexible and elegant multi-platform test framework for Kotlin with extensive assertions and integrated property testing.

Testing Styles

junit 스타일 지원, JS에서 많이 사용하는 jest와 유사한 테스트 스타일 지원, goovy로 작성하는 spock과도 유사한 형태의 behavior test 스타일도 각기 모두 지원한다.

테스트 스타일 종류

  • Fun Spec(Scala)
  • Describe Spec(JavaScript framework)
  • Should Spec(kotest)
  • String Spec(kotest)
  • Behavior Spec(BDD)
  • Free Spec(Scala)
  • Word Spec(Scala)
  • Feature Spec(Cucumber)
  • Expect Spec(kotest)
  • Annotation Spec(Junit)

class Calculator() {
    fun add(a: Int, b: Int) = a + b
    fun minus(a: Int, b: Int): Int {
        if (a < 0 || b < 0) {
            throw IllegalArgumentException()
        }
        return a - b
    }
}

class CalculatorAnnotationSpecTest : AnnotationSpec() {
    @Test
    fun `10 + 20 더하면, 30이 반환 되어야 한다`() {
        val calculator = Calculator()
        val result = calculator.add(10, 20)
        result shouldBe 30
    }
}

data class PythagTriple(val a: Int, val b: Int, val c: Int)

fun isPythagTriple(a: Int, b: Int, c: Int): Boolean = a * a + b * b == c * c
class MyTests : FunSpec({
    withData(
        PythagTriple(3, 4, 5),
        PythagTriple(6, 8, 10),
        PythagTriple(8, 15, 17),
        PythagTriple(7, 24, 25)
    ) { (a, b, c) ->
        isPythagTriple(a, b, c) shouldBe true
    }
})

class CalculatorFunSpecTest : FunSpec({
    test("10 + 20 더하면, 30이 반환 되어야 한다.") {
        val calculator = Calculator()
        val result = calculator.add(10, 20)
        result shouldBe 30
    }

    context("a + b = sum Test") {
        val calculator = Calculator()
        table(
            headers("a", "b", "sum"),
            row(1, 2, 3),
            row(-1, 1, 0),
            row(0, 0, 0),
            row(100, 200, 300),
            row(-50, 25, -25)
        ).forAll { a, b, sum ->
            test("$a + $b = $sum") {
                calculator.add(a,b) shouldBe sum
            }
        }
    }
})

class CalculatorTest : StringSpec({
    val calculator = Calculator()

    val testCases = table(
        headers("a", "b", "sum"),
        row(1, 2, 3),
        row(-1, 1, 0),
        row(0, 0, 0),
        row(100, 200, 300),
        row(-50, 25, -25)
    )

    forAll(testCases) { a, b, sum ->
        "$a + $b = $sum" {
            calculator.add(a, b) shouldBe sum
        }
    }
})

class CalculatorStringSpecTest : StringSpec({
    "10 + 20 더하면, 30이 반환 되어야 한다." {
        val calculator = Calculator()
        val result = calculator.add(10, 20)
        result shouldBe 30
    }
})

class CalculatorShouldSpecTest : ShouldSpec({
    should("10 + 20 더하면, 30이 반환") {
        val calculator = Calculator()
        val result = calculator.add(10, 20)
        result shouldBe 30
    }
})

class CalculatorDescribeSpecTest : DescribeSpec({
    describe("calculator 테스트") {
        it("10 + 20 더하면, 30이 반환 되어야 한다.") {
            val calculator = Calculator()
            val result = calculator.add(10, 20)
            result shouldBe 30
        }

        it("10 + 30 더하면, 40이 반환 되어야 한다.") {
            val calculator = Calculator()
            val result = calculator.add(10, 30)
            result shouldBe 40
        }
    }
})

class CalculatorBehaviorSpecTest : BehaviorSpec({
    given("calculator 테스트") {
        `when`("10 + 20 더하면,") {
            then("30이 반환 되어야 한다.") {
                val calculator = Calculator()
                val result = calculator.add(10, 20)
                result shouldBe 30
            }
        }

        `when`("10 + 30 더하면,") {
            then("40이 반환 되어야 한다.") {
                val calculator = Calculator()
                val result = calculator.add(10, 30)
                result shouldBe 40
            }
            xthen("해당 구문은 실행하지 않는다.") {
                val calculator = Calculator()
                val result = calculator.add(10, 30)
                result shouldBe 40
            }
        }
    }
})

Matcher

module

matcher - core
  • shouldBe
  • shouldBeTrue
  • shouldBeFalse
  • shouldThrow
  • shouldBeNull
  • shouldContain
  • shouldContainAll
  • shouldBeEmpty
  • etc...

intellij plugin

플러그인을 설치 해주면, 실행이 매우 편리해진다. IDEA 내장으로 넣어줘요..

settings

test-framework

testImplementation 'io.kotest:kotest-runner-junit5:$version'

tasks.withType<Test>().configureEach {
   useJUnitPlatform()
}

data driven test (aka. parameterized test)

ref. https://kotest.io/docs/framework/datatesting/data-driven-testing.html

testImplementation 'io.kotest:kotest-framework-datatest:5.5.5'
class MyTests : FunSpec({
  context("Pythag triples tests") {
    withData(
      PythagTriple(3, 4, 5),
      PythagTriple(6, 8, 10),
      PythagTriple(8, 15, 17),
      PythagTriple(7, 24, 25)
    ) { (a, b, c) ->
      isPythagTriple(a, b, c) shouldBe true
    }
  }
})

다양한 케이스를 좀 알아 보자.

class Calculator() {
    fun add(a: Int, b: Int) = a + b
    fun minus(a: Int, b: Int): Int {
        if (a < 0 || b < 0) {
            throw IllegalArgumentException()
        }
        return a - b
    }
}

위와 같은 클래스를 테스트 해본다.

class CalculatorFunSpecTest : FunSpec({
    test("10 + 20 더하면, 30이 반환 되어야 한다.") {
        val calculator = Calculator()
        val result = calculator.add(10, 20)
        result shouldBe 30
    }

    context("a + b = sum Test") {
        val calculator = Calculator()
        table(
            headers("a", "b", "sum"),
            row(1, 2, 3),
            row(-1, 1, 0),
            row(0, 0, 0),
            row(100, 200, 300),
            row(-50, 25, -25)
        ).forAll { a, b, sum ->
            test("$a + $b = $sum") {
                calculator.add(a,b) shouldBe sum
            }
        }
    }
})

class CalculatorTest : StringSpec({
    val calculator = Calculator()

    val testCases = table(
        headers("a", "b", "sum"),
        row(1, 2, 3),
        row(-1, 1, 0),
        row(0, 0, 0),
        row(100, 200, 300),
        row(-50, 25, -25)
    )

    forAll(testCases) { a, b, sum ->
        "$a + $b = $sum" {
            calculator.add(a, b) shouldBe sum
        }
    }
})

위와 같이 table 값을 input으로 받아 테스트 제목으로 출력 하는 것도 가능하다. junit을 대체 할 수 있어보인다.

assertions library

testImplementation 'io.kotest:kotest-assertions-core:$version'

property testing

testImplementation 'io.kotest:kotest-property:$version'

spring kotest extension

testImplementation 'io.kotest.extensions:kotest-extensions-spring:$version'

mocking의 경우에는 mokk를 사용하는 것이 좋다.

https://mockk.io/

반응형