테스팅
테스팅 (testing)
코드를 테스트하는 것은 소프트웨어 개발의 핵심 부분이에요. 테스트는 코드 동작을 자동으로·반복적으로 검증해 주고, 코드가 기대대로 동작한다는 것을 보장해 줘요.
Raku에서 Test 모듈이 테스팅 프레임워크를 제공하는데, Raku 공식 스펙테스트 스위트(spectest)도 이걸 사용해요. 테스팅 함수는 Test Anything Protocol에 맞는 출력을 내보내요. 보통은 싱크 컨텍스트(sink context)에서 쓰죠.
ok check-name($meta, :$relaxed-name), "name has a hyphen rather than '::'"
하지만 모든 함수는 테스트가 성공했는지 나타내는 Boolean도 돌려줘서, 테스트가 실패했을 때 메시지를 출력하는 데 쓸 수 있어요.
ok check-name($meta, :$relaxed-name), "name has a hyphen rather than '::'" \
or diag "\nTo use hyphen in name, pass :relaxed-name to check-name\n";
테스트 작성하기(Writing tests)
테스트를 다르게 구성할 수도 있지만, 전형적인 Raku 관례는 테스트가 프로젝트 기본 디렉터리의 t 디렉터리 아래에 있다는 점이에요. 전형적인 테스트 파일은 이렇게 생겼습니다.
use Test; # a Standard module included with Rakudo
use lib 'lib';
plan $num-tests;
# .... tests
done-testing; # optional with 'plan'
내장 Test 모듈을 로드하고, 다른 라이브러리가 어디 있는지 지정해요. 그런 다음 실행할 테스트 수를 plan으로 선언하죠. (그래야 프레임워크가 기대보다 많거나 적은 테스트가 실행됐는지 알려줘요.) 테스트가 끝나면 done-testing으로 프레임워크에 끝났다고 알려줘요.
스레드 안전성(Thread safety)
Test 모듈의 루틴은 스레드 안전하지 않아요. 여러 스레드에서 동시에 테스팅 루틴을 쓰려고 하면 안 돼요. TAP 출력이 순서가 뒤섞여서 그것을 해석하는 프로그램을 혼란스럽게 만들 수 있기 때문이에요.
현재로서는 스레드 안전하게 만들 계획이 없어요. 스레드 테스팅이 꼭 필요하다면, 테스팅 요구에 Test 대신 쓸 만한 에코시스템 모듈을 찾아볼 수 있어요.
테스트 실행하기(Running tests)
테스트는 명령줄에서 테스트 파일 이름을 지정해 개별적으로 실행할 수 있어요.
$ raku t/test-filename.rakutest
디렉터리의 모든 테스트를 재귀적으로 실행하려면 prove6를 쓰면 돼요. 사용 전에 zef로 설치해야 해요.
$ zef install App::Prove6
배포 디렉터리에서 prove6를 이렇게 실행할 수 있어요.
$ prove6 --lib t/
t/ 인자는 테스트가 들어 있는 디렉터리를 지정하고, --lib 옵션은 lib 디렉터리를 Raku 배포 경로에 포함시키기 위해 전달돼요. raku 명령의 -Ilib 인자와 같은 뜻이죠. prove6 사용에 대한 더 많은 문서는 그 페이지를 참고하세요.
첫 번째 실패에서 테스트 스위트를 중단하려면 RAKU_TEST_DIE_ON_FAIL 환경 변수를 설정하세요.
$ RAKU_TEST_DIE_ON_FAIL=1 raku t/test-filename.rakutest
같은 변수를 테스트 파일 안에서도 쓸 수 있어요. Test 모듈을 로드하기 전에 설정하세요.
BEGIN %*ENV<RAKU_TEST_DIE_ON_FAIL> = 1;
use Test;
...
참고: Rakudo 2020.05 릴리스 이전에는 이 기능을 켜는 데 PERL6_TEST_DIE_ON_FAIL 환경 변수를 사용했는데, 여전히 지원되지만 더 이상 사용되지 않아요(deprecated).
마이크로초 단위의 테스트 타이밍을 출력하려면 RAKU_TEST_TIMES 환경 변수를 설정하세요.
$ env RAKU_TEST_TIMES=1 raku -e 'use Test; plan 1; pass sleep(1);'
1..1
# between two timestamps 0 microseconds
ok 1 -
# t=1000721
같은 변수를 테스트 파일 안에서도 쓸 수 있어요. Test 모듈을 로드하기 전에 설정하세요.
BEGIN %*ENV<RAKU_TEST_TIMES> = 1;
use Test;
...
참고: Rakudo 2020.05 릴리스 이전에는 이 기능을 켜는 데 PERL6_TEST_TIMES 환경 변수를 사용했는데, 여전히 지원되지만 더 이상 사용되지 않아요.
테스트 플랜(Test plans)
테스트 플랜은 plan으로 앞으로 할 테스트 수를 선언해요(경우에 따라 건너뛸 수도 있음). 플랜이 선언되지 않았다면 done-testing으로 테스트가 끝났음을 선언해요.
반환 값 테스트하기(Testing return values)
Test 모듈은 주어진 표현식의 반환 값을 검사하고 표준 테스트 출력을 만드는 다양한 함수를 내보내요. 실제로 그 표현식은 보통 단위 테스트하려는 함수나 메서드 호출이에요. ok와 nok는 True와 False를 검사해요. 하지만 가능하면 아래의 전문화된 비교 테스트 함수를 쓰는 편이 좋아요. 비교가 실패할 때 더 유용한 진단 출력을 찍어줄 수 있기 때문이에요.
문자열 비교
is와 isnt는 다루는 객체(또는 클래스)에 따라 적절한 연산자로 동등성을 검사해요.
근사 수치 비교
is-approx는 일정한 정밀도(절대 또는 상대)로 숫자를 비교해요. 정밀도가 내부 표현에 따라 달라지는 수치 값에 유용할 수 있어요.
구조 비교
구조 역시 is-deeply로 비교할 수 있는데, 비교된 객체의 내부 구조가 같은지 검사해요.
임의 비교
cmp-ok로 어떤 종류의 비교든 쓸 수 있어요. 비교에 사용할 함수나 연산자를 인자로 받죠.
객체 타입
isa-ok는 객체가 특정 타입인지 검사해요.
메서드 이름
can-ok는 객체에 그 특정 메서드가 있는지 검사하는 데 써요.
역할
does-ok는 주어진 변수가 특정 Role을 수행할 수 있는지 검사해요.
정규식
like와 unlike는 정규식으로 검사해요. 전자는 일치가 있으면 통과하고, 후자는 일치가 없으면 통과해요.
모듈 테스트하기(Testing modules)
모듈은 use-ok로 시험적으로 로드되며, 로드에 실패하면 테스트가 실패해요.
예외 테스트하기(Testing exceptions)
dies-ok와 lives-ok는 코드를 테스트하는 상반된 방법이에요. 전자는 예외를 던지는지, 후자는 그렇지 않은지를 검사하죠. throws-like는 코드가 인자로 받은 특정 예외를 던지는지 검사하고, fails-like는 코드가 특정 타입의 Failure를 돌려주는지 비슷하게 검사해요. eval-dies-ok와 eval-lives-ok는 테스트하기 전에 평가되는 문자열에 대해 비슷하게 동작해요.
의도적인 종료 테스트하기(Testing intentional exits)
Raku 패키지 개발자에게는 때로 특정 조건에서 코드 경로의 의도적인 종료를 제공하는 것이 유용할 수 있어요. 그 종료는 POSIX 범위 1-127의 고유한 숫자 종료 코드로 나타낼 수 있어요. test exits-ok가 바로 그것을 제공해요.
Rakudo 2026.01 릴리스부터 유효해요. 사용법은 다음 서브루틴에서 보여줘요.
sub apply-factor-to(Numeric $num --> Numeric) {
unless $num > 1 {
say "FATAL: sub 'apply-factor-to' input is $num, it must be > 1";
exit(3);
}
$num * 2.0;
}
이렇게 테스트할 수 있어요.
use Test;
exits-ok {
my $x = apply-factor-to -2;
}, 3, "got the expected signal 3 for invalid input";
테스트 묶기(Grouping tests)
하위 테스트(subtests) 묶음의 결과는 모든 하위 테스트가 ok일 때만 ok예요. subtest로 묶습니다.
테스트 건너뛰기(Skipping tests)
때때로 테스트는 아직 실행할 준비가 안 됐어요. 예를 들어 기능이 아직 구현되지 않았을 수도 있는데, 그 경우 테스트를 todo로 표시할 수 있어요. 아니면 어떤 기능이 특정 플랫폼에서만 동작하는 경우도 있어요. 그렇다면 다른 플랫폼에서는 테스트를 skip하면 돼요. skip-rest는 인자로 주어진 특정 개수 대신 나머지 테스트를 건너뛰고, bail-out은 메시지와 함께 테스트를 그냥 종료해요.
수동 제어(Manual control)
위에서 문서화한 편의 기능이 여러분의 요구에 맞지 않는다면, 다음 함수로 테스트 하니스 출력을 직접 제어할 수 있어요. pass는 테스트가 통과했음을 말하고, diag는 (어쩌면 유용한) 메시지를 출력해요.