uju's Tech

[kotest] Property-based Testing 본문

Programming/Kotlin

[kotest] Property-based Testing

ujusy 2022. 5. 8. 07:20

이번에 next step (https://edu.nextstep.camp/)  에서 이펙티브 코틀린~을 수강하게 되면서 kotest 로 테스트를 작성해보게 되었다.

 

강의가 Junit5 을 기본으로 설명하고있어서 kotest와 Junit5 중 어떤 프레임워크를 사용할지 고민을 했다.

 

결론은 kotest 를 도전해보기로했다.

 

일단 kotest를 선택한 이유는

  • kotlin을 위한 테스트프레임워크이기 때문에 kotlin dsl 을 완벽하게 지원하는점
  • (Junit5, kotest 둘 다 사용해보지 않는 사람의 시점에서) Junit5에 비해서 간결하고 보기 좋고 Kotlin스럽게 코드를 작성할 수 있는 점

등으로 kotest 를 선택해서 테스트를 진행했다.

 

그리고 kotest는 테스트 스타일을 지정할 수 있는데 나는 FreeSpec 으로 설정 후 진행했다.

 

Property-based test는 하나의 테스트에 여러 값으로 테스트를 하고싶은 경우 사용하기 좋다. 여러 입력값을 매 테스트로 만드는건..😱

 

공식 문서

https://kotest.io/docs/proptest/property-based-testing.html

 

Property-based Testing | Kotest

Kotest is split into several subprojects which can be used independently. One of these subprojects is the property test framework.

kotest.io

bulid.gradle.kts 의존성 추가

testImplementation("io.kotest", "kotest-property", "5.2.3")

Test functions

For All

  • 람다식이 반환하는 값이 참인지 체크 
forAll<String, String> { a, b ->
     (a + b).length == a.length + b.length
}

Check All

  • assertion 을 사용하여 검증할 때 사용
  • assertion이 모두 통과해야한다.
checkAll<String, String> { a, b ->
 	a + b shouldHaveLength a.length + b.length
}

 

나는 assertion 을 사용하여 검증을 하고싶었으므로 check All을 사용했다.  

 

두 함수 모두

👉🏻  14개 인자까지 가능

👉🏻  default로 내부적으로 1000번 테스트가 수행되고 테스트 횟수는 명시적으로 변경 가능

 

만약 호출하는 함수에 필요한 인자가 3개라면 다음과 같이 작성해줄 수 있다.

checkAll<String, String, Int> { a, b, c ->
 	test(a,b,c) shouldBe "result"
}

만야게 100번 랜덤한 테스트가 수행되기를 원한다면 다음과 같이 작성가능하다.

checkAll<String, String, Int> (100){ a, b, c ->
 	test(a,b,c) shouldBe "result"
}

 

<> 에는 14개의 인자까지 들어갈 수 있는데 인자의 타입을 작성해주면 된다. 

{.. } 에는 인자의 변수를 임의로 작성하여 테스트하는 함수를 호출한다. 

 

이렇게만 작성해주면 kotest에서 랜덤값을 자동으로 생성해주어 1000번의 테스트를 수행해준다.

 

해당 방식의 좋은 점은 개발자가 예상치 못했던 input 등 예외 케이스를 잡아줄 수 있는 점이라고 생각하는데 공식 문서에는 오히려 문제가 개발자가 예측할 수 없는 점이 될 수도 있다~ 라고 작성되어있다.

실제로 string 인자로 테스트를 했는데 한자가 들어와서 "오잉? 한자가.. 들어온다고..?" 했다. ㅋㅋ

Generators

예를 들어 완전한 랜덤값이 아닌 모든 숫자인데 0을 제외하고 돌리고싶은 경우 등.. 한정되어있는 범위 내에서 테스트하고 싶을 때는
kotest generator에서 제공하는 arbitrary generator을 사용해주면 된다.

 

Arb 에 대한 다양한 기본 옵션 들은 아래 공식문서를 참고하면 된다.

https://kotest.io/docs/proptest/property-test-generators-list.html

 

Generators List | Kotest

This page lists all current generators in Kotest. There are two types of generator: arbitrary

kotest.io

 

예시1. 

첫 번째 인자에는 Double 타입 , 두 번째 인자에는 0을 제외한 Double 타입이 들어가기 원하는 경우

"테스트" {
    val nonZeroDoubleArb = Arb.positiveDouble().merge(Arb.negativeDouble())

    checkAll(Arb.double(), nonZeroDoubleArb) { acc, operand -> 
       ...
    }
}

예시2.

문자 중 제외하고 싶은 문자가 있는 경우. 아래 예시는 Generator Operations(https://kotest.io/docs/proptest/generator-operations.html) 를 사용해주었다.

"테스트" {
    val validCharSet: Set<Char> = setOf('a', 'b')

    val invalidChar = CharRange(Char.MIN_VALUE, Char.MAX_VALUE) - validCharSet

    val invalidCharArb = arbitrary { rs ->
        val index: Int = rs.random.nextInt(invalidChar.size)
        invalidChar[index].toString()
    }

    checkAll(invalidCharArb, Arb.double(), Arb.double()) { a, b, c ->
        ...
    }
}

Generator List와 Generator Operations를 적절하게 조합해서 사용해줄 수 있다.

 

 

작성한 테스트 코드

https://github.com/next-step/kotlin-racingcar/pull/439/files#diff-ee4c0fbd1b7a1ec3989cfcd55b6878ef1e8fea85ecd6529f902508eb31b049cc

 

Step2: 문자열 계산기 by ujusy · Pull Request #439 · next-step/kotlin-racingcar

안녕하세요! stpe2 문자열 계산기 리뷰요청드립니다 :) 아래 두 가지를 중점으로 개발을 진행했습니다. 읽기 좋은 코드 코틀린스러운 코드를 작성해보기 코틀린이 아직 많이 어색하지만 이것 저

github.com

 

Comments