Kotest 프로퍼티 테스트 함수(Property Test Functions)

Kotest에서 프로퍼티 테스트를 실행하는 데 사용되는 두 가지 변형 함수는 forAllcheckAll 두가지가 있다.

For All

첫 번째로 forAll은 프로퍼티(Property)를 테스트하는 n-arity 함수 (a, ..., n) -> Boolean을 받는다. 모든 입력 값에 대해 함수가 참을 반환하면 테스트가 통과된다.

import io.kotest.core.spec.style.StringSpec
import io.kotest.property.forAll

class PropertyExample: StringSpec({
    "String size" {
        forAll<String, String> { a, b ->
            (a + b).length == a.length + b.length
        }
    }
})

이 함수는 인자 유형에 대한 유형 매개변수를 허용하며, 최대 14개의 아리티(arity)를 지원한다. Kotest는 이러한 유형 매개변수를 사용하여 적절한 유형의 임의 값을 제공하는(생성하는) 제너레이터를 찾는다.

예를 들어, forAll<String, Int, Boolean> { a, b, c -> }는 인자 a가 임의의 문자열, 인자 b가 임의의 int, 인자 c가 임의의 부울인 3-arity 프로퍼티 테스트이다.

Check All

두 번째로 checkAll은 입력에 대해 간단히 단언문을 실행할 수 있는 n-arity 함수(a, ..., n) -> Unit을 받는다. 이 접근 방식은 예외가 발생하지 않으면 테스트가 유효한 것으로 간주한다. 다음은 동일한 예제를 체크올을 사용하여 동일한 방식으로 다시 작성한 것이다.

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.string.shouldHaveLength
import io.kotest.property.checkAll

class PropertyExample: StringSpec({
    "String size" {
        checkAll<String, String> { a, b ->
            a + b shouldHaveLength a.length + b.length
        }
    }
})

두 번째 접근 방식은 부울을 반환하는 것보다 더 일반적인 목적이지만, 첫 번째 접근 방식은 이 라이브러리에 영감을 준 원래 하스켈 라이브러리에서 유래한 것이다.

Iterations

기본적으로 Kotest는 프로퍼티 테스트를 1000회 실행한다. 테스트 메서드를 호출할 때 반복 횟수를 지정하여 이를 쉽게 사용자 지정할 수 있다.

테스트를 10,000회 실행한다고 가정해 보겠다.

import io.kotest.core.spec.style.StringSpec
import io.kotest.property.checkAll

class PropertyExample: StringSpec({
    "a many iterations test" {
        checkAll<Double, Double>(10_000) { a, b ->
            // test here
        }
    }
})

제너레이터 지정하기

앞 예제에서 Kotest가 유형 매개변수에 따라 자동으로 값을 제공하는 것을 보았다. 필요한 유형에 대한 값을 생성하는 제너레이터를 찾아서 이를 수행한다. 예를 들어, 자동으로 제공되는 정수 제너레이터는 음수, 양수, 무한대, 0 등 가능한 모든 값에서 임의의 정수를 생성한다.

이는 기본적인 테스트에 적합하지만 샘플 공간에 대한 더 많은 제어를 원하는 경우가 많다. 예를 들어, 특정 범위의 숫자에 대해서만 함수를 테스트하고 싶을 수 있다. 그런 경우 제너레이터를 수동으로 지정해야 한다.

import io.kotest.core.spec.style.StringSpec
import io.kotest.property.Arb
import io.kotest.property.arbitrary.int
import io.kotest.property.forAll

class PropertyExample4 : StringSpec({

    fun isDrinkingAge(a: Int): Boolean =
        a in 19..Int.MAX_VALUE

    "is allowed to drink in Chicago" {
        forAll(Arb.int(21..150)) { a ->
            isDrinkingAge(a) // assuming some function that calculates if we're old enough to drink
        }
    }
    "is allowed to drink in London" {
        forAll(Arb.int(18..150)) { a ->
            isDrinkingAge(a) // assuming some function that calculates if we're old enough to drink
        }
    }
})

두 개의 테스트를 생성하고 각 테스트에서 적절한 int 범위를 가진 forAll 함수에 제너레이터를 전달한 것을 볼 수 있다.

내장된 제너레이터 목록은 여기를 참조하라.


참조




최종 수정 : 2024-04-14