Kotest 테스팅 스타일(Testing Styles)

Kotest에는 다양한 테스트 스타일(Testing Styles)을 제공하고 있다. 여기서는 다양한 테스트 스타일에 대해서 설명한다.

테스트 스타일

Kotest에서는 다양한 테스트 스타일(Testing Styles)을 제공한다. 여기서는 Kotest에서 지원하는 테스트 스타일 10가지를 소개한다.

Testing Style 영감을 받은 출처
Fun Spec ScalaTest
String Spec A Kotest original
Should Spec A Kotest original
Describe Spec Javascript frameworks and RSpec
Behavior Spec BDD frameworks
Word Spec ScalaTest
Free Spec ScalaTest
Feature Spec Cucumber
Expect Spec A Kotest original
Annotation Spec JUnit

각각의 스타일은 특정한 문법을 따르며, 다양한 상황에 맞게 선택하여 사용할 수 있다. 각 테스트 스타일은 각각의 특성과 장단점을 가지고 있으며, 프로젝트의 요구 사항에 따라 적절한 스타일을 선택하여 사용할 수 있다.

아래는 각 테스트 스타일에 대해 설명하고 기본적인 사용법은 이중에 하나를 상속받아 작성되는데, 이를 예제 코드로 알아보도록 하겠다.

FunSpec

FunSpec은 함수 기반의 테스트 스타일이다. 각 테스트 케이스를 함수로 작성하고, 테스트 본문을 함수 내에 작성한다.

FunSpec을 사용하면 테스트를 설명하는 문자열 인수를 사용하여 test라는 함수를 호출한 다음 테스트 자체를 람다로 호출하여 테스트를 만들 수 있다. 확실하지 않은 경우 이 스타일을 사용하는 것이 좋다.

아래는 FunSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe

class FunSpecTest : FunSpec({
    test("FunSpec 예제 - 덧셈 테스트") {
        val result = 2 + 2
        result shouldBe 4
    }
})

일반적인 방법 외에도 xcontextxtest 변형을 사용하여 테스트를 비활성화할 수 있다.

class MyTests : FunSpec({
    context("this outer block is enabled") {
        xtest("this test is disabled") {
            // test here
        }
    }
    xcontext("this block is disabled") {
        test("disabled by inheritance from the parent") {
            // test here
        }
    }
})

StringSpec

StringSpec은 문자열 기반의 테스트 스타일이다. 자연어와 유사한 구문으로 테스트를 작성할 수 있다. 주로 각 테스트 케이스를 문자열로 작성하고, 테스트 본문을 중괄호 {} 내에 작성한다.

StringSpec은 구문을 최소한으로 줄여준다. 테스트 코드에 문자열 뒤에 람다 식을 작성하기만 하면 된다.

아래는 StringSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe

class StringSpecTest : StringSpec({
    "StringSpec 예제 - 덧셈 테스트" {
        val result = 2 + 2
        result shouldBe 4
    }
})

테스트에 구성 추가할 수도 있다.

class MyTests : StringSpec({
    "strings.length should return size of string".config(enabled = false, invocations = 3) {
        "hello".length shouldBe 5
    }
})

ShouldSpec

ShouldSpecFuncSpec과 비슷하지만 테스트 대신 should라는 키워드의 구문을 사용하여 작성한다. 테스트 케이스의 동작을 명세하는 데에 주로 사용된다.

아래는 ShouldSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.ShouldSpec
import io.kotest.matchers.shouldBe

class ShouldSpecTest : ShouldSpec({
    should("ShouldSpec 예제 - 덧셈 테스트") {
        val result = 2 + 2
        result shouldBe 4
    }
})

테스트는 하나 이상의 컨텍스트 블록에 중첩될 수도 있다:

class MyTests : ShouldSpec({
    context("String.length") {
        should("return the length of the string") {
            "sammy".length shouldBe 5
            "".length shouldBe 0
        }
    }
})

일반적인 방법 외에도 xcontextxshould 변형을 사용하여 테스트를 비활성화할 수 있다.

class MyTests : ShouldSpec({
    context("this outer block is enabled") {
        xshould("this test is disabled") {
            // test here
        }
    }
    xcontext("this block is disabled") {
        should("disabled by inheritance from the parent") {
            // test here
        }
    }
})

DescribeSpec

DescribeSpec 테스트 스타일은 describe / it 키워드를 사용하기 때문에 Ruby 또는 자바스크립트 배경을 가진 사람들에게 익숙한 스타일을 제공한다. describe 구문을 사용하여 테스트 케이스를 그룹화하고, 테스트는 하나 이상의 설명 블록에 중첩되어야 한다. 테스트 케이스를 설명하는 데에 주로 사용된다.

아래는 DescribeSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe

class DescribeSpecTest : DescribeSpec({
    describe("DescribeSpec 예제") {
        it("덧셈 테스트") {
            val result = 2 + 2
            result shouldBe 4
        }
    }
})

일반적인 방법 외에도 xdescribexit 변형을 사용하여 테스트를 비활성화할 수 있다:

class MyTests : DescribeSpec({
    describe("this outer block is enabled") {
        xit("this test is disabled") {
            // test here
        }
    }
    xdescribe("this block is disabled") {
        it("disabled by inheritance from the parent") {
            // test here
        }
    }
})

BehaviorSpec

행위 기반의 테스트(BDD, Behavior Driven Development) 스타일의 작성 방법이다. given, when, then 블록을 사용하여 테스트를 작성한다. 주요 기능이나 동작에 초점을 맞추어 테스트 케이스를 작성한다.

아래는 BehaviorSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe

class BehaviorSpecTest : BehaviorSpec({
    Given("BehaviorSpec 예제") {
        When("덧셈 테스트") {
            val result = 2 + 2
            Then("2와 2를 더하면 4가 되어야 한다") {
                result shouldBe 4
            }
        }
    }
})

Given, WhenAnd 키워드를 사용하여 깊이를 더할 수도 있다:

class MyTests : BehaviorSpec({
    given("a broomstick") {
        and("a witch") {
            `when`("The witch sits on it") {
                and("she laughs hysterically") {
                    then("She should be able to fly") {
                        // test code
                    }
                }
            }
        }
    }
})

일반적인 방법 외에도 xgiven, xwhenxthen 변형을 사용하여 테스트를 비활성화할 수 있다:

class MyTests : BehaviorSpec({
    xgiven("this is disabled") {
        When("disabled by inheritance from the parent") {
            then("disabled by inheritance from its grandparent") {
                // disabled test
            }
        }
    }
    given("this is active") {
        When("this is active too") {
            xthen("this is disabled") {
               // disabled test
            }
        }
    }
})

WordSpec

WordSpec은 문자열 기반의 테스트 스타일이다. 자연어와 유사한 구문을 사용하여 테스트를 작성한다. should 키워드를 사용하여 컨텍스트 문자열 뒤에 테스트를 중첩하는 데 사용한다. 각 테스트 케이스를 문자열로 작성하고, 테스트 본문을 should 블록 내에 작성한다.

아래는 WordSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.shouldBe

class WordSpecTest : WordSpec({
    "WordSpec 예제 - 덧셈 테스트" should {
        "2와 2를 더하면 4가 되어야 한다" {
            val result = 2 + 2
            result shouldBe 4
        }
    }
})

또한 다른 수준의 중첩을 추가할 수 있도록 허용할 때 when 키워드를 지원한다. when은 Kotlin에서의 키워드이므로 백틱 또는 대문자 변형을 사용해야 한다는 점에 주의해야 한다.

class MyTests : WordSpec({
    "Hello" When {
        "asked for length" should {
            "return 5" {
                "Hello".length shouldBe 5
            }
        }
        "appended to Bob" should {
            "return Hello Bob" {
                "Hello " + "Bob" shouldBe "Hello Bob"
            }
        }
    }
})

FreeSpec

FreeSpec은 자유로운 테스트 스타일이다. 테스트 케이스 간의 의존성을 줄이고, 독립적으로 테스트를 작성할 수 있다.

FreeSpec을 사용하면 외부 테스트의 경우 키워드 -(마이너스)를 사용하고 최종 테스트의 경우 테스트 이름만 사용하여 임의의 깊이 수준을 중첩할 수 있다:

아래는 FreeSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe

class FreeSpecTest : FreeSpec({
    "FreeSpec 예제 - 덧셈 테스트" - {
        val result = 2 + 2
        "2와 2를 더하면 4가 되어야 한다" {
            result shouldBe 4
        }
    }
})

FeatureSpec

FeatureSpec은 기능(Feature) 기반의 테스트 스타일이다. 각 테스트 케이스를 기능 또는 시나리오에 맞추어 작성한다.

FeatureSpec을 사용하면 기능 및 시나리오를 사용할 수 있으며, 이는 오이를 사용해 본 사람들에게 친숙할 것이다. 오이와 완전히 똑같지는 않지만 키워드는 스타일을 모방하고 있다.

아래는 FeatureSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.FeatureSpec
import io.kotest.matchers.shouldBe

class FeatureSpecTest : FeatureSpec({
    feature("FeatureSpec 예제") {
        scenario("덧셈 테스트") {
            val result = 2 + 2
            result shouldBe 4
        }
    }
})

일반적인 방법 외에도 xfeature 및 xscenario 변형을 사용하여 테스트를 비활성화할 수 있다:

class MyTests : FeatureSpec({
    feature("this outer block is enabled") {
        xscenario("this test is disabled") {
            // test here
        }
    }
    xfeature("this block is disabled") {
        scenario("disabled by inheritance from the parent") {
            // test here
        }
    }
})

ExpectSpec

테스트 스펙을 expect 구문을 사용하여 작성한다. 예상 결과를 명시하는 데에 주로 사용된다.

ExpectSpecFunSpecShouldSpec과 유사하지만 expect 키워드를 사용한다.

아래는 ExpectSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.ExpectSpec
import io.kotest.matchers.shouldBe

class ExpectSpecTest : ExpectSpec({
    context("ExpectSpec 예제") {
        expect("덧셈 테스트") {
            val result = 2 + 2
            2 + 2 shouldBe 4
        }
    }
})

테스트는 하나 이상의 컨텍스트 블록에 중첩될 수도 있다:

class MyTests : ExpectSpec({
    context("a calculator") {
        expect("simple addition") {
            // test here
        }
        expect("integer overflow") {
            // test here
        }
    }
})

일반적인 방법 외에도 xcontextxexpect 변형을 사용하여 에스트를 비활성화할 수 있다.

class MyTests : ExpectSpec({
    context("this outer block is enabled") {
        xexpect("this test is disabled") {
            // test here
        }
    }
    xcontext("this block is disabled") {
        expect("disabled by inheritance from the parent") {
            // test here
        }
    }
})

AnnotationSpec

어노테이션 기반의 테스트 스타일이다. 특정 어노테이션을 사용하여 테스트를 작성한다.

JUnit에서 마이그레이션하는 경우 AnnotationSpec은 JUnit 4/5와 같은 어노테이션을 사용하는 스펙이다. 스펙 클래스에 정의된 함수에 @Test 어노테이션을 추가하기만 하면 된다.

JUnit와 비슷하게 before tests/specsafter tests/specs에 어노테이션을 추가할 수 있다.

@BeforeAll / @BeforeClass
@BeforeEach / @Before
@AfterAll / @AfterClass
@AfterEach / @After

테스트를 무시하려면 @Ignore를 사용한다.

아래는 AnnotationSpec에 대한 예제 코드이다:

package com.devkuma.kotest.tutorial.testingstyles

import io.kotest.core.spec.style.AnnotationSpec
import io.kotest.matchers.shouldBe

class AnnotationSpecExample : AnnotationSpec() {

    @BeforeEach
    fun beforeTest() {
        println("Before each test")
    }

    @Test
    fun test1() {
        1 shouldBe 1
    }

    @Test
    fun test2() {
        3 shouldBe 3
    }
}

테스트 구조화

Kotest는 테스트 구조화를 위해서 사용하는 함수는 context 블록을 사용하여 테스트 계층 또는 그룹으로 정의하여 테스트 케이스가 작성할 수 있다. 이렇게 함으로써 테스트를 논리적으로 분류하고 관련 테스트 케이스를 함께 관리할 수 있다.

예를 들어, 다음은 테스트 그룹을 사용하여 테스트를 구조화하는 예시이다:

import io.kotest.core.spec.style.FunSpec

class ContextTest : FunSpec({

    context("Calculator tests") {
        test("Addition") {
            // Test logic for addition
        }

        test("Subtraction") {
            // Test logic for subtraction
        }
    }

    context("String tests") {
        test("Length") {
            // Test logic for string length
        }

        test("Concatenation") {
            // Test logic for string concatenation
        }
    }
})

위의 예제에서는 context 블록을 사용하여 테스트 분류하여 나누고 있다.

context 블록은 FunSpec, ShouldSpec, ExpectSpec에서만 사용할 수 있다.


참조




최종 수정 : 2024-04-23