티스토리 뷰

kotlin

kotest 훑어보기

LichKing 2022. 10. 17. 21:12

처음 코틀린을 접한지 5년이 다되어가고 있다. 물론 실제 현업에서 사용한건 그 반정도 밖에 안되지만 말이다. 그동안 코틀린이라는 언어에 대한 본연의 기능과 그 외 코틀린용 라이브러리, 자바 프레임워크와 코틀린과의 궁합 등등에 대해 학습은 하면서도 테스트만큼은 계속 Junit 만을 이용해오고 있었다. mock 라이브러리인 mockito 와는 이런저런 궁합문제로 진즉에 mockk 로 넘어갔지만 Junit 과 assertion 라이브러리인 AssertJ 에 대해선 크게 불편함을 못 느꼈던게 그 이유가 아닐까 싶다. 여튼 kotest 자체는 오래전부터 알고있었으나 뒤늦게 한번 훑어보기로했다.

 

kotest 는 크게 3개의 모듈로 구분된다. 이 모듈들에 대해 간단하게 알아보자.

 

1. Test Framework

테스트를 작성할때 Test framework 와 Assertion 을 구분해서 말하지 않는 경우가 많다. "보통 Junit 으로 테스트를 작성한다" 정도로 표현하기 때문이다. 하지만 엄밀히 둘은 구분할 수 있는데 예를 들어 요즘 Junit 으로 테스트를 작성할때는 Junit 을 프레임워크로 사용하고, Assertion 은 AssertJ 를 이용하는 경우가 많다. kotest 도 마찬가지로 둘 모두 지원한다.

 

2. Assertions Library

Test framework 외에 Assertion 으로만 kotest 를 이용할 수도 있다. 이 둘을 구분한다는건 Junit 을 이용하면서 kotest 를 Assertion 으로만 이용할 수도 있고, kotest 를 프레임워크로 이용하면서 AssertionJ 를 Assertion 으로 이용할 수도 있다는 의미다.

 

3. Property Testing

우리가 보통 작성하는 테스트를 Example based test 라고 표현한다. 이와는 조금 다른쪽으로 접근하여 Property based test 라는 방식이 있고, kotest 가 그를 지원한다. 이건 여기서 얘기하기엔 너무 긴 내용이므로 다른 블로그를 링크하고, 여기선 위 두 개에 대해 알아본다.

 

# Test Framework

kotest 는 다양한 방식의 테스트 작성법을 지원하고 있다. xxSpec 이라는 형태의 추상 클래스를 제공함으로써 각 방식을 지원하고 있으며 원하는 xxSpec 클래스를 테스트 클래스에서 상속받는 형태로 작성하면 된다.

 

FunSpec

class CalculatorTest : FunSpec({
    test("1과 2를 더하면 3이다") {
        // arrange
        val calculator = Calculator()

        // act
        val result = calculator.plus(1, 2)

        // assert
        result shouldBe 3
    }
})

예제 정도라 크게 어려울건 없다. 추상 클래스명에서 알 수 있듯 함수를 이용한 방식의 테스트이며 Assertion 으로도 kotest 를 이용했다. AssertJ 를 이용하면 이렇게 작성할 수 있다.

class CalculatorTest : FunSpec({
    test("1과 2를 더하면 3이다") {
        // arrange
        val calculator = Calculator()

        // act
        val result = calculator.plus(1, 2)

        // assert
        assertThat(result).isEqualTo(3)
    }
})

한 가지 흥미로운 부분은 테스트 코드를 슈퍼 클래스 생성자 부분에 넣는다는 점이다. 이는 아래에서 알아볼 AnnotationSpec 외 모든 추상 클래스가 동일한데, 사실상 AnnotationSpec 이 Junit 때문에 나온걸 감안하면 생성자에 넣는 방식이 kotest 가 지향하는 점이라고 볼 수 있을 것 같다. FunSpec 은 ScalaTest 에 영향을 받았다고 언급하고 있다.

 

Should Spec

class CalculatorTest : ShouldSpec({
    should("1과 2를 더하면 3이다") {
        // arrange
        val calculator = Calculator()

        // act
        val result = calculator.plus(1, 2)

        // assert
        result shouldBe 3
    }
})

FunSpec 과 크게 다른 부분은 없다. 여러 방식을 테스트해보면 알겠지만 생성자 안에 넣는 방식으로 인해 각 방식에 대한 네임스페이스가 제공되는걸 알 수 있다.

 

AnnotationSpec

class CalculatorTest : AnnotationSpec() {
    @Test
    fun `1과 2를 더하면 3이다`() {
        // arrange
        val calculator = Calculator()

        // act
        val result = calculator.plus(1, 2)

        // assert
        result shouldBe 3
    }
}

우리한테 가장 친숙한 방식이고, 유일하게 클래스 바디에 적는 방식이다. 당연히 Junit 의 영향을 받았다고 언급되고 있다. 참고로 이때 사용하는 @Test 애노테이션은 Junit 이 아니고 kotest 에 있는걸 사용해야한다. Junit 과 명칭이 비슷한 케이스가 많으므로 잘 확인하고 쓰자. kotest 를 사용하기로 결정했다면 Junit 을 아예 의존성에서 제거해서 혼동이 없게 하는걸 추천한다.

 

이 외에도 다양한 방식을 제공하고 있으므로 이는 공식문서에서 확인하자.

 

# Assertions Library

shouldBe

1+2 shouldBe 3

위에서부터 살펴본 가장 기본적인 Assertion 이다.

 

shouldNotBe

1+2 shouldNotBe 4

shouldContain

"hello kotest" shouldContain "kotest"
listOf(1, 2, 3) shouldContain 2

참고로 위 두 shouldContain 은 정의된 패키지가 다르다.

 

이 외 다양한 Matcher 들을 지원하고 있으니 찾아보면 유용하게 이용할 수 있다.

 

# 기타

짜잘한 내용으로 kotest 로 작성한 테스트가 intellij 에서 실행버튼을 보이지 않을 수 있다. kotest 로 작성한 테스트를 intellij 에서 실행하기 위해서는 plugin 이 필요하다.

댓글
댓글쓰기 폼