Arturo 트랙에서 테스트하기

Exercism에서 Arturo 연습 문제를 테스트하는 방법을 알아봐요


Arturo 트랙은 모든 연습 문제의 테스트에 Unitt 3.0.0 단위 테스트 패키지를 사용해요. 이 패키지는 최신 RSpec 스타일 문법과 예전 XUnit 스타일 문법을 모두 지원하는 테스트 프레임워크를 제공해요. 로컬 연습 문제 폴더에서 arturo tester.art나 exercism test를 실행하면 처음 실행할 때 인터넷에서 패키지를 설치해요. 또는 arturo --package install unitt 3.0.0을 실행해서 Unitt를 설치할 수도 있어요. 첫 번째 테스트만 실행되고 그 결과만 알려줘요. 그 테스트가 통과하면 다음 테스트의 skip을 해제하고, 모든 테스트가 통과할 때까지 테스트를 다시 실행해요. RSpec과 XUnit 방식 모두 각각 it.skip 또는 test.skip에서 .skip을 지우기만 하면 돼요. 모든 테스트가 통과하면 풀이를 제출해요. 다만 테스트 러너는 건너뛴 테스트까지 모든 테스트를 실행한다는 점을 기억해요.

시작하기

내려받은 연습 문제 폴더에는 파일이 여러 개 있지만, 꼭 필요한 파일은 다음 세 개예요.

leap/
├── src/leap.art         # Your solution file
├── tests/test-leap.art  # Test cases for the exercise
└── tester.art           # Test runner entry point

arturo tester.art를 실행하면 Unitt를 설정한 다음 tests/test-leap.art 안의 테스트를 실행해요. 테스트는 풀이가 담긴 src/leap.art를 불러온 다음, 특정 입력에 대해 풀이가 무엇을 반환해야 하는지 검증하는 일련의 단언을 해요.

풀이 파일

isLeap?: function [year][
    panic "please implement the isLeap? function"
]

연습 문제를 시작하면 풀이 파일에 기본 구조가 담긴 템플릿이 함께 있어요. Leap의 이 예시에서 파일에는 필요한 isLeap? 함수의 최소한의 정의가 들어 있고, 특히 테스트 실행을 멈추는 panic이 있어요. panic 줄을 지우고 isLeap?의 구현을 작성하기 시작해요.

RSpec 테스트 구조

import.version:3.0.0 {unitt}!
import {src/leap}!

describe "Leap" [
    it "a year not divisible by 4 is a common year" [
        expects.be:'false? @[isLeap? 2015]
    ]

    it.skip "a year divisible by 4 and not divisible by 100 is a leap year" [
        expects.be:'true? @[isLeap? 1996]
    ]
]
  • describe는 공통된 무언가를 검증하는 테스트 케이스 블록을 정의해요. 이런 블록은 중첩할 수 있어요.
  • it과 it.skip은 각각 실행되거나 건너뛰는 하나의 테스트 케이스를 정의해요.
  • expects는 테스트 케이스의 핵심인 단언을 시작해요. expects.be는 단언을 만드는 데 쓰이는 조건 함수를 받아요.

expects.be:'false? @[isLeap? 2015]에서 테스트 스위트는 isLeap? 함수가 2015년에 대해 false를 반환해야 한다고 단언해요. 다르게 쓰면 expects.be:'equal? @[false isLeapYear? 2015]인데, 여기서는 isLeapYear? 2015의 반환값이 false와 같다고 단언해요. 두 값을 비교하는 이 긴 형태는 Arturo 트랙에서 가장 흔한 단언 방식이에요. 블록 안에서는 항상 결과값보다 기댓값이 먼저 와요.

XUnit 테스트 구조

suite "Leap" [
    test "a year not divisible by 4 is a common year" [
        result: isLeap? 2015
        assert -> false = result
    ]

    test.skip "a year divisible by 4 and not divisible by 100 is a leap year" [
        result: isLeap? 1996
        assert -> true = result
    ]
]

Unitt v1에서 도입된 XUnit 스타일 API는 하위 호환성을 위해 지원되며, 단언이 덜 유연하다는 점을 빼면 최신 RSpec 스타일 API와 대체로 같아요.

  • suite는 공통된 무언가를 검증하는 테스트 케이스 블록을 정의해요. 이런 블록은 중첩할 수 있어요.
  • test와 test.skip은 각각 실행되거나 건너뛰는 하나의 테스트 케이스를 정의해요.
  • assert는 테스트 케이스의 핵심인 단언을 시작해요. 뒤따르는 -> 오른쪽의 표현식은 참이어야 해요.

XUnit 테스트에서는 보통 assert 줄 앞에서 결과를 나타내는 변수와, 필요에 따라 기댓값을 나타내는 변수를 만들어요.

테스트 출력

RSpec API를 쓰든 XUnit API를 쓰든 테스트 출력은 대체로 비슷해요. 차이는 각 테스트 이름 아래에 출력되는 내용뿐이에요. XUnit은 평가된 값을 사용해 단언을 보고하기 때문에 false = false가 보여요. 반면 RSpec은 expects와 함께 false? 함수를 사용했기 때문에 false? false로 보고해요.

처음부터 살펴보면, 시작 파일의 panic을 지우지 않았다면 Arturo는 오류를 보고하고 테스트를 하나도 평가하지 않아요.

Uncaught error in tests/test-leap.art:
#[
        output: {
                
                ══╡ Program Error ╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ <script> ══
                
                  Please implement the isLeap? function
                
                
        }
        code: 1
]

panic을 대체하면 Arturo는 테스트 안에서 이루어진 각 단언의 상태를 보고해요. 이제 첫 번째 테스트는 통과하지만 두 번째 테스트는 건너뛰어요.

======== .unitt/tests/test-leap.art ========

Describe: Leap

    ✅ - assert that a year not divisible by 4 is a common year
         ✅: false? false

    ⏩ - assert that a year divisible by 4 and not divisible by 100 is a leap year
         skipped!


========== Summary ==========

⏏️   TOTAL: 2 assertions
✅  PASSED: 1 assertion
⏩ SKIPPED: 1 assertion
❌  FAILED: 0 assertion

========== ======= ==========

두 번째 테스트의 skip을 직접 해제하면 두 테스트 모두 실행돼요. 하지만 현재 isLeap? 코드 구현은 false만 반환하기 때문에 두 번째 테스트는 단언에 실패해요.

======== .unitt/tests/test-leap.art ========

Describe: Leap

    ✅ - assert that a year not divisible by 4 is a common year
         ✅: false? false

    ❌ - assert that a year divisible by 4 and not divisible by 100 is a leap year
         ❌: true? false


========== Summary ==========

⏏️   TOTAL: 2 assertions
✅  PASSED: 1 assertion
⏩ SKIPPED: 0 assertion
❌  FAILED: 1 assertion

========== ======= ==========

테스트의 skip을 해제할수록 이 과정이 이어져요. 모든 테스트가 통과하면 연습 문제를 끝낸 것이고 풀이를 제출할 수 있어요. 테스트 러너는 건너뛴 테스트든 아니든 모든 테스트를 로컬에서 실행한다는 점을 기억해요.