Kotest 조건부 테스트(Conditional evaluation)

테스트를 비활성화하는 방법에는 여러 가지가 있다. 테스트에서 하드 코딩하거나, 런타임에 조건부로 비활성화할 수도 있다.

Config

Kotest는 테스트에 구성 플래그를 설정하여 테스트를 비활성화할 수 있도록 지원한다. 이런 구성의 플래그는 enabled, enabledIf, enabledOrReasonIf가 있다.

enabled flags

config 함수에 매개변수의 enabledfalse로 설정하여, 테스트 케이스를 비활성화할 수 있다. 반대로 true 넣어서 활성화한다.

매개변수의 enabled를 설정하는 예시를 보도록 하겠다:

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

class ConfigEnabledTest : StringSpec({

    "항상 실행".config(enabled = true) {
        // 이 테스트는 항상 실행되지 않는다.
        1 + 1 shouldBe 2
    }

    "항상 실행 안됨".config(enabled = false) {
        // 이 테스트는 항상 실행되지 않는다.
        1 + 1 shouldBe 3
    }
})

false로 설정된 경우는 항상 실행이 되고, true로 설정한 경우는 실행이 되지 않는다.

동일한 메커니즘을 사용하여 특정 조건에서만 테스트를 실행할 수 있다. 예를 들어, Apache Commons LangSystemUtils.IS_OS_LINUX를 사용하여 Linux 환경에서만 특정 테스트를 실행하는 등으로 활용할 수도 있다.

"리눅스인 경우만 실행".config(enabled = IS_OS_LINUX) {
    // 이 테스트는 리눅스인 경우에는 실행된다.
}

"맥인 경우만 실행".config(enabled = IS_OS_MAC) {
    // 이 테스트는 맥인 경우에는 실행된다.
}

Enabled if

테스트가 호출될 때마다 평가되는 함수를 사용하려면 enabledIf를 사용할 수 있다. 이 함수는 (TestCase) -> Boolean 형태이며, 런타임 시에 테스트의 활성화 또는 비활성화 여부를 평가할 때 테스트에 액세스할 수 있다.

예를 들어 리눅스 환경이거나, ‘danger’이라는 단어로 시작하는 모든 테스트를 비활성화하려는 경우에는 아래와 같이 할 수 있다:

import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.EnabledIf
import org.apache.commons.lang3.SystemUtils.IS_OS_LINUX

class ConfigEnabledIf : StringSpec({

    val disableDangerOnNotLinux: EnabledIf = { !it.name.testName.startsWith("danger") || IS_OS_LINUX }

    "danger will robinson".config(enabledIf = disableDangerOnNotLinux) {
        // test here
    }

    "very safe will".config(enabledIf = disableDangerOnNotLinux) {
        // test here
    }
})

Enabled or Reason If

활성화 플래그의 세 번째 변형으로, 테스트가 비활성화된 이유를 반환할 수 있는 enabledOrReasonIf라는 변형이 있다. 이 변형은 (TestCase) -> Enabled 형태를 가지며, 여기서 Enabled는 건너뛰기 이유를 포함할 수 있는 유형이다. 이 사유의 문자열은 테스트 보고서에 전달된다.

예를 들어, 앞의 ‘danger’ 예제를 다음과 같이 다시 작성할 수 있다:

import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.Enabled
import io.kotest.core.test.TestCase
import org.apache.commons.lang3.SystemUtils

class ConfigEnabledOrReasonIfTest : StringSpec({

    val disableDangerOnFridays: (TestCase) -> Enabled = {
        if (it.name.testName.startsWith("danger") || SystemUtils.IS_OS_LINUX)
            Enabled.disabled("It's a linux, and we don't like danger!")
        else
            Enabled.enabled
    }

    "danger Will Robinson".config(enabledOrReasonIf = disableDangerOnFridays) {
        // test here
    }

    "safe Will Robinson".config(enabledOrReasonIf = disableDangerOnFridays) {
        // test here
    }
})

아래 결과는 콘솔에 표시된 내용이다:

Process finished with exit code 0

It's a linux, and we don't like danger!

Focus

Kotest에서 Focus는 특정 테스트를 실행할 때 우선순위를 부여하는 기능이다. Focus를 사용하면 특정 테스트나 테스트 그룹에 초점을 맞추어 실행할 수 있다. 이는 개발자가 특정 부분에 집중하고 디버깅 및 개발을 더 효율적으로 수행할 수 있도록 도와준다.

Focus를 설정하는 방법은 간단한다. 테스트 함수나 테스트 그룹의 앞에 f:를 붙이면 해당 테스트나 테스트 그룹이 Focus된다. 즉, 해당 테스트 및 해당 범위 내에 정의된 모든 하위 테스트들만 실행되고 나머지는 테스트들은 실행되지 않는다.

예를 들어, 다음은 Focus를 사용한 예제 코드이다:

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

class FocusTest: StringSpec({
    "이 테스트는 Focus되지 않는다" {
        // 이 테스트는 실행되지 않는다.
        1 + 1 shouldBe 2
    }

    // 이 테스트는 Focus되어 실행된다.
    "f:를 사용한 Focus 예제" {
        1 + 1 shouldBe 2
    }

    "이 테스트도 Focus되지 않는다" {
        // 이 테스트는 실행되지 않는다.
        1 + 1 shouldBe 2
    }
})

위의 코드에서 "f:를 사용한 Focus 예제" 테스트만 Focus되어 실행된다. 나머지 테스트는 실행되지 않는다. 이는 개발자가 주요 부분에만 집중하여 개발을 더욱 효율적으로 수행할 수 있도록 도와준다.

그리고, 중첩 테스트는 상위 테스트가 실행된 후에만 검색되기 때문에 포커스 모드는 중첩 테스트에 대해서는 작동하지 않는다.

Bang

Kotest에서 Bang(!)은 해당 테스트를 무시하고 실행하지 않도록 지정하는 데 사용된다. 이는 특정 테스트가 현재 상황에서 실행되지 않아야 할 때 유용하다. 특히 개발 중인 테스트를 일시적으로 비활성화할 때 유용하다. 특정 테스트를 완전히 삭제하지 않고 나중에 다시 활성화할 수 있다.

Bang은 테스트 함수나 테스트 그룹의 앞에 사용된다.

예를 들어, 다음은 Bang을 사용한 예제 코드이다:

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

class BangTest : StringSpec({
    "!이 테스트는 실행되지 않는다" {
        // 이 테스트는 실행되지 않는다.
        1 + 1 shouldBe 2
    }

    "이 테스트는 실행된다" {
        // 이 테스트는 실행된다.
        1 + 1 shouldBe 2
    }
})

위의 코드에서 접두어로 !가 붙은 "!이 테스트는 실행되지 않는다"라는 테스트는 실행되지 않는다. 다른 나머지 테스트는 실행된다.

이런 방식으로 Bang을 사용하여 특정 테스트를 임시적으로 비활성화할 수 있으며, 필요할 때 다시 활성화할 수 있다.

X-Method

Kotest에서 X-Method는 테스트를 비활성화하는 데 사용되는 메타데이터이다. 특정 테스트 함수나 테스트 그룹의 앞에 x를 붙이면 해당 테스트는 실행되지 않는다. 이는 특정 테스트를 일시적으로 비활성화하고 나중에 다시 활성화할 수 있도록 한다.

예를 들어, 다음은 X-Method를 사용한 예제 코드이다:

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

class XMethodTest : DescribeSpec({

    xdescribe("이 블록과 그 하위 항목은 이제 비활성화되었다.") {
        it("이 테스트는 실행되지 않는다") {
            // disabled test
        }
    }
})

위의 코드에서 X-Method를 사용하여 특정 테스트 그룹과 테스트를 비활성화하였다. xdescribe는 각각 비활성화된 그룹과 테스트를 나타낸다. 나머지 테스트는 실행된다.

이런 방식으로 X-Method를 사용하여 특정 테스트나 테스트 그룹을 임시적으로 비활성화할 수 있으며, 나중에 필요할 때 다시 활성화할 수 있다.

@Ignored

Kotest에서 @Ignored 어노테이션은 모든 테스트 함수를 비활성화하는 데 사용된다. 이 어노테이션을 사용하면 해당 테스트 함수는 실행되지 않는다. 이는 일시적으로 특정 테스트를 비활성화하고 나중에 다시 활성화할 수 있도록 한다.

예를 들어, 다음은 @Ignored 어노테이션을 사용한 예제 코드이다:

package com.devkuma.conditional.evaluation

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

@Ignored
class IgnoredTest : StringSpec({
    "이 테스트는 실행되지 않는다" {
        // 이 테스트는 실행되지 않는다.
        1 + 1 shouldBe 3
    }
}) {
    init {
        error("boom") // 스펙 생성되지 않으므로 이 오류가 발생하지 않는다.
    }
}

위의 코드에서 @Ignored 어노테이션을 사용하여 모든 테스트를 비활성화하였다.

이렇게하여 특정 테스트 함수를 일시적으로 비활성화할 수 있으며, 필요할 때 다시 활성화할 수 있다.

@EnabledIf

Kotest에서 @EnabledIf 어노테이션은 특정 조건이 참일 때에만 테스트 함수를 활성화하는 데 사용된다. 이 어노테이션을 사용하면 특정 조건이 충족되는 경우에만 테스트를 실행할 수 있다.

@EnabledIf 어노테이션을 사용하기 위해서는 EnabledCondition 인터페이스를 구현하는 클래스를 만들어야 한다. 이 인터페이스는 isEnabled 메서드를 포함하며, 이 메서드에서는 특정 조건을 판별하여 참 또는 거짓을 반환한다.

예를 들어, 다음은 @EnabledIf 어노테이션을 사용하여 특정 조건에 따라 테스트를 활성화하는 예제 코드이다:

package com.devkuma.conditional.evaluation

import io.kotest.core.annotation.EnabledCondition
import io.kotest.core.annotation.EnabledIf
import io.kotest.core.spec.Spec
import io.kotest.core.spec.style.StringSpec

import io.kotest.matchers.shouldBe
import kotlin.reflect.KClass

@EnabledIf(MyEnabledCondition::class)
class EnabledIfTest2 : StringSpec({
    "이 테스트는 특정 조건이 참일 때 실행된다" {
        // 이 테스트는 MyEnabledCondition에서 지정한 조건이 참일 때 실행된다.
        1 + 1 shouldBe 2
    }
})

object MyEnabledCondition : EnabledCondition {
    override fun enabled(kclass: KClass<out Spec>): Boolean {
        // 여기서는 특정 조건을 판별하여 참 또는 거짓을 반환한다.
        return true // 특정 조건이 참이면 true를 반환하여 테스트를 활성화한다.
    }
}

위의 코드에서 MyEnabledCondition 객체는 EnabledCondition 인터페이스를 구현하고 있다. enabled 메서드에서는 특정 조건을 판별하여 참 또는 거짓을 반환한다. @EnabledIf 어노테이션에는 이러한 조건을 지정하는데 사용된다. 따라서 특정 조건이 참일 때에만 해당 테스트가 실행된다.

이렇게하여 특정 조건에 따라 테스트를 조건부로 활성화할 수 있으며, 테스트가 실행될 조건을 유연하게 제어할 수 있다.

Gradle에서 필터링하기

gradle을 통해 JUnit 플랫폼 러너를 통해 Kotest를 실행할 때 Kotest는 테스트 필터링을 위한 표준 gradle 구문을 지원한다. 빌드 스크립트에서 또는 --tests 명령줄 옵션을 통해 필터링을 활성화할 수 있다.

tasks.test {
    filter {
        //include all tests from package
        includeTestsMatching("com.devkuma.somepackage.*")
    }
}
$ ./gradlew test --tests 'com.devkuma.somepackage*'

클래스 수준만 필터링 가능하다. 개별 함수에는 지정할 수 없다.


참고




최종 수정 : 2024-04-14