ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 14-1. 코틀린 테스팅 : 코테스트 명세
    Study(종료)/Kotlin 22.09.13 ~ 12.18 2022. 12. 16. 14:37

    14장 - 코틀린 테스팅

     

    14-1. 코테스트 명세

    14-2. 단언문(assertion)

    14-3.  픽스쳐와 설정


    사실 혼자 앱을 만들 때는 테스트 케이스를 만들기보다는

    심심할 때 마다 내가 만든 앱을 켜서 재대로 동작하는지 확인하고

    만약 오류가 난다면 어떤 작업을 헀을 때 어떤 오류가 나는지 확인하고

    혼자 내가 작성한 코드를 생각해보며 왜 이런 오류가 나는지 고민해보고

    만약 생각이 안난다면 디버그 모드를 통해 실제 데이터의 흐름을 보며 오류의 원인을 파악헀다.

    틀린 방법이라고는 생각하지 않지만, 이런 식으로 디버깅을 하면

    데이터의 흐름을 break Point를 찍어서 일일히 확인해 봐야 하기도 하고

    코드를 다시 한번 분석해야 되기 때문에 시간이 꽤나 오래 걸리는 작업이었다.

     

    그래서 최근에 TDD 얘기가 많이 나오기도 하고

    개인적으로도 테스트 케이스를 작성하는 방법에 대해서 궁금했던 찰나에

    마지막장인 14장에서 테스트에 대한 내용이 있어서 다시 또 열심히 작성해 봐야겠다.


    테스트 프레임워크는 소프트웨어 개발 생명 주기 전반에서 소프트웨어 품질을 유지할 수 있도록

    도움을 주는 재사용 가능한 테스트 코드의 작성을 돕는다고 한다.

    잘 설계된 자바 상호 운용성 덕분에 코틀린 개발자는 JVM 플랫폼을 대상으로 하는

    JUnit, TestNG, Mockito 등의 수많은 테스트 도구를 활용할 수 있고,

    코틀린 생태계에서도 코틀린 언어를 활용한

    테스트 코드를 작성할 수 있게 도움을 주는 몇몇 프레임워크가 생겼는데,

    이들 중에서 오픈 소스인 코테스트에 대한 내용을 작성해 보겠다.

     

    14-1. 코테스트 명세

    책에서 사용하는 코테스트인 io.kotest:kotest-runner-junit5-jvm:4.5.0을 사용해 진행해 보겠다.

     

    1) 코테스트 시작하기

    프로젝트 뷰 에서 F4를 누르면 프로젝트 설정을 볼 수 있고,

    이곳에서 Libraries > +버튼 > From maven을 누른 뒤 위에 작성한 코테스트를 추가하면 된다.

    이후 프로젝트 뷰 에서 src와 같은 경로에 test 폴더를 추가한 뒤

    우클릭 > Mark Directoy as > Test Source Root를 선택해 테스트 소스 폴더로 지정한다.

    IDEA가 해당 디렉토리 안의 내용을 테스트에 사용할 소스 파일로 인식하면

    테스트 디렉토리의 색이 녹색으로 바뀐다.

     

    kotest 플러그인도 추가로 설치했다.

    플러그인은 Settings 대화창의 plugins 탭에서 kotest를 검색해 설치하면 된다.


    2) 명세 스타일

    코테스트는 여러 명세 스타일을 지원한다.

    프로젝트에서 여러 스타일을 섞어 쓰거나 AbstractSpec 클래스를 이용해 나만의 명세 스타일을 만들 수도 있다.

     

    테스트 케이스를 정의하려면 테스트 명세 클래스 중 하나를 상속하고,

    클래스 생성자에 테스트를 추가하거나 상위 클래스 생성자에 전달하는 람다 안에 테스트를 추가한다.

     

    아래는 StringSpec 클래스를 사용하는 코드이다.

    import io.kotest.matchers.shouldBe
    import io.kotest.core.spec.style.StringSpec
    
    class NumbersTest : StringSpec({
        "2 + 2 should be 4" { (2 + 2) shouldBe 4 }
        "2 * 2 should be 4" { (2 * 2) shouldBe 4 }
    })

    StringSpec에서는 테스트에 대한 설명이 들어있는 문자열 뒤에 람다를 추가해 개별 테스트를 작성한다.

    위 코드에서 실제 검증 코드는 shouldBe 중위 연산자 함수를 사용하며,

    이 함수는 수신 객체로 받은 값과 인자로 받은 값이 다르면 예외를 던진다.

    StringSpec는 모든 테스트가 한 클래스 안에 들어가고

    모든 테스트가 같은 수준에 정의돼 있는 평평한 구조를 만들어낸다.

    테스트 블록을 다른 블록 안에 넣으려고 시도하면 프레임워크가 런타임에 예외를 발생시키면서 실패한다.

     

    WordSpec 클래스를 이용해 더 복잡한 레이아웃을 만들 수 있다.

    이를 이용해 2단계로 이뤄진 계층 구조를 만들 수 있다.

    이 때 각 테스트는 StringSpec과 비슷하며, should() 함수 호출에 의해 각각의 그룹으로 묶인다.

    import io.kotest.matchers.shouldBe
    import io.kotest.core.spec.style.WordSpec
    
    class NumbersTest : WordSpec({
        "1 + 2" should {
            "be equal to 3" { (1 + 2) shouldBe 3 }
            "be equal to 2 + 1" { (1 + 2) shouldBe (2 + 1) }
        }
    })

    should() 호출을 When() 또는 'when'() 으로 감싸면 계층을 3단계로 구성할 수도 있다.

    import io.kotest.matchers.shouldBe
    import io.kotest.core.spec.style.WordSpec
    
    class NumbersTest2 : WordSpec({
        "Addition" When {
            "1 + 2" should {
                "be equal to 3" { (1 + 2) shouldBe 3 }
                "be equal to 2 + 1" { (1 + 2) shouldBe (2 + 1) }
            }
        }
    })

    테스트 계층을 원하는 만큼 깊게 만들고 싶을 경우 FunSpec 클래스는 테스트 코드를 test() 함수 호출로 묶는다.

    import io.kotest.matchers.shouldBe
    import io.kotest.core.spec.style.FunSpec
    
    class NumbersTest3 : FunSpec({
        test("0 should be equal to 0") { 0 shouldBe 0 }
        context("Arithmetic") {
            context("Addition") {
                test("2 + 2 should be 4") { (2 + 2) shouldBe 4 }
            }
            context("Multiplication") {
                test("2 * 2 should be 4") { (2 * 2) shouldBe 4 }
            }
        }
    })

    test와 context 블록을 어떤 깊이에서도 사용할 수 있다.

    단, test 블록을 test 블록 안에 쓸 수는 없다.

     

    위 내용들을 정리하면 

    StringSpec을 이용해 테스트 계층이 한단계인 테스트 레이아웃을

    WordSpec을 이용해 테스트 계층이 두 단계인 테스트 레이아웃을

    FunSpec을 이용해 테스트 계층이 여러 단계인 레이아웃을 만들 수 있다고 이해했다.

     

    FunSpec과 유사한 내용을 더 보자면,

    DescribeSpec는 describe() / context() / it() 을 사용하고

    ShouldSpec은 context() / should()를, FeatureSpec는 feature()를 사용하며

    BehaviorSpec은 Given() / When() / Then() / And()를 사용한다.

     

    AnnotationSpec은 @Test 어노테이션에 의존하며 JUnit / TestNG와 비슷하다.

    import io.kotest.matchers.shouldBe
    import io.kotest.core.spec.style.AnnotationSpec
    
    class NumbersTest3 : AnnotationSpec() {
        @Test
        fun shouldBe4() { (2 + 2) shouldBe 4}
    
        @Test
        fun shouldBe4_() { (2 * 2) shouldBe 4}
    }

    책에서는 아래와 같이 되있는데 실행이 되지 않아 위 코드로 변경해 실행했다.

    import io.kotest.matchers.shouldBe
    import io.kotest.core.spec.style.AnnotationSpec
    
    class NumbersTest3 : AnnotationSpec() {
        @Test fun '2 + 2 should be 4'() { (2 + 2) shouldBe 4 }
        @Test fun '2 * 2 should be 4'() { (2 * 2) shouldBe 4 }
    }

    다음글

    14-2. 단언문(assertion)

    반응형

    댓글

Designed by Tistory.