tcltest — 테스트 하니스 지원 코드와 유틸리티

tcltest — 테스트 하니스 지원 코드와 유틸리티

Tcl 코드를 제대로 검증하려면 테스트를 체계적으로 실행하고 결과를 비교하는 도구가 필요해요. tcltest는 테스트 스위트를 만들고 실행하는 데 쓰는 유틸리티 명령 모음이에요. Tcl 라이브러리 자체의 내장 명령들도 이 패키지로 만든 테스트 스위트로 검증해요.

출처: Tcl 공식 문서 - tcltest

본문

tcltest 패키지는 테스트 스위트를 만드는 데 유용한 여러 유틸리티 명령을 제공해요. tcltest가 제공하는 모든 명령은 위 SYNOPSIS에 표시된 대로 ::tcltest 네임스페이스에 정의되고 거기서 export돼요. 아래 절들에서 모든 명령은 간결성을 위해 단순 이름으로 설명돼요.

tcltest의 중심 명령은 테스트를 정의하고 실행하는 test예요. test로 테스트하는 것은 Tcl 스크립트를 평가해 그 결과를 몇몇 옵션으로 구성·제어되는 예상 결과와 비교하는 것을 포함해요. tcltest가 제공하는 다른 몇몇 명령은 test의 구성과 많은 테스트 명령을 테스트 스위트로 모으는 것을 관장해요.

아래 CREATING TEST SUITES WITH TCLTEST 절에 tcltest 명령으로 Tcl 활성 코드의 테스트 스위트를 만드는 확장 예제가 있어요.

명령(Commands)

  • test name description ?-option value ...?: 이름 name, 설명 description으로 테스트를 정의하고 가능하면 실행해요. 테스트의 namedescription은 tcltest의 옵션으로 구성된 대로 테스트 중 test가 보고하는 메시지에 쓰여요. test의 나머지 option value 인자들이 테스트를 정의해요. 실행할 스크립트, 실행 조건, 예상 결과, 예상·실제 결과를 비교하는 수단을 포함해요. 유효한 옵션과 그것이 테스트를 어떻게 정의하는지에 대한 완전한 설명은 아래 TESTS를 보세요. test 명령은 빈 문자열을 반환해요.
  • test name description ?constraints? body result: 이 형태의 test는 tcltest 패키지 버전 1용으로 작성된 테스트 스위트를 지원하고, 흔한 용법에 더 단순한 인터페이스를 제공하기 위해 존재해요. "test name description -constraints constraints -body body -result result"와 같아요. test의 다른 모든 옵션은 기본값을 취해요. constraints가 생략되면 모든 옵션이 -로 시작하므로 이 형태를 첫 번째와 구분할 수 있어요.
  • loadTestedCommands: 호출자의 컨텍스트에서 configure -loadconfigure -loadfile이 지정한 스크립트를 평가해요. 그 스크립트 평가의 결과를 반환해요(스크립트가 일으킨 오류 포함). 이 명령과 관련 구성 옵션을 사용해 테스트할 명령을 테스트 스위트를 실행하는 인터프리터에 제공해요.
  • makeFile contents name ?directory?: directory 디렉터리 기준으로 name이라는 파일을 만들고 contents를 시스템 인코딩으로 그 파일에 써요. contents가 줄바꿈으로 끝나지 않으면 파일이 줄바꿈으로 끝나도록 줄바꿈이 추가돼요. 시스템 인코딩을 쓰므로 이 명령은 텍스트 파일을 만드는 데만 적합해요. 파일은 removeFile이 먼저 제거하지 않으면 다음 cleanupTests 평가에서 제거돼요. directory의 기본값은 configure -tmpdir의 디렉터리예요. 만들어진 파일의 전체 경로를 반환해요. 테스트에 필요한 텍스트 파일을 내용을 채워 만들 때 쓰세요.
  • removeFile name ?directory?: name이 가리키는 파일을 강제로 제거해요. 이 파일 이름은 directory 기준이어야 해요. directory의 기본값은 configure -tmpdir이에요. 빈 문자열을 반환해요. makeFile이 만든 파일을 지울 때 쓰세요.
  • makeDirectory name ?directory?: directory 디렉터리 기준으로 name이라는 디렉터리를 만들어요. 디렉터리는 removeDirectory가 먼저 제거하지 않으면 다음 cleanupTests 평가에서 제거돼요. directory의 기본값은 configure -tmpdir이에요. 만들어진 디렉터리의 전체 경로를 반환해요. 테스트에 존재해야 하는 디렉터리를 만들 때 쓰세요.
  • removeDirectory name ?directory?: name이 가리키는 디렉터리를 강제로 제거해요. 이 디렉터리는 directory 기준이어야 해요. directory의 기본값은 configure -tmpdir이에요. 빈 문자열을 반환해요. makeDirectory가 만든 디렉터리를 지울 때 쓰세요.
  • viewFile file ?directory?: read -nonewline이 반환하는 것처럼, 마지막 줄바꿈을 뺀 file 내용을 반환해요. 이 파일 이름은 directory 기준이어야 해요. directory의 기본값은 configure -tmpdir이에요. 테스트가 만든 파일의 내용을, 예상 결과와 대조하기 위해 그 테스트의 결과로 바꾸는 편리한 방법으로 쓰세요. 파일 내용은 시스템 인코딩으로 읽으므로 텍스트 파일에만 유용해요.
  • cleanupTests: 여러 테스트를 실행한 뒤 정리하고 요약하기 위한 것이에요. 보통 테스트 파일당 한 번, 파일 끝에서 모든 테스트를 마친 뒤 호출돼요. 최상의 효과를 위해, 테스트 파일 평가 중 일찍 오류가 발생해도 cleanupTests가 평가되도록 해야 해요. 실행한 테스트에 대한 통계를 출력하고 마지막 cleanupTests 이후 makeDirectorymakeFile이 만든 파일을 제거해요. 마지막 cleanupTests 이후 configure -tmpdir 디렉터리에 만들어졌지만 makeFile이나 makeDirectory가 만들지 않은 파일·디렉터리 이름은 outputChannel에 출력돼요. 이 명령은 전역 env 배열이 설명하는 원래 셸 환경도 복원해요. 빈 문자열을 반환해요.
  • runAllTests: 여러 파일/디렉터리에 걸친 전체 테스트 스위트를 실행하기 위한 메인 명령이에요. tcltest의 구성 옵션이 관장해요. runAllTests로 가능한 많은 변형에 대한 완전한 설명은 아래 RUNNING ALL TESTS를 보세요.

구성 명령(Configuration Commands)

  • configure: tcltest가 지원하는 구성 옵션 목록을 반환해요. 전체 목록은 아래 CONFIGURABLE OPTIONS를 보세요.
  • configure option: 지원되는 구성 옵션 option의 현재 값을 반환해요. option이 지원되지 않는 구성 옵션이면 오류를 발생해요.
  • configure option value ?-option value ...?: 각 구성 옵션 option의 값을 해당 value로 순서대로 설정해요. 옵션이 지원되지 않거나, value가 해당 옵션에 유효한 값이 아니거나, 값이 제공되지 않으면 오류를 발생해요. 오류가 발생하면 configure의 동작이 중단되고 이후 option value 인자들은 처리되지 않아요. ::env(TCLTEST_OPTIONS) 환경 변수가(package require tcltest로) tcltest 패키지가 로드될 때 존재하면 그 값은 configure에 넘길 인자 목록으로 취급돼요. 이를 통해 구성 옵션의 기본값을 환경으로 설정할 수 있어요.
  • customMatch mode script: modetest-match 옵션의 새 유효 값으로 등록해요. test-match mode 옵션이 넘겨지면, 스크립트 script가 평가되어 테스트 본문 평가의 실제 결과를 예상 결과와 비교해요. 매칭을 수행하기 위해 스크립트는 예상 결과와 실제 결과라는 두 단어를 추가로 붙여 완성되고, 완성된 스크립트는 전역 네임스페이스에서 평가돼요. 완성된 스크립트는 결과가 일치하는지 여부를 나타내는 boolean 값을 반환할 것으로 기대돼요. test의 내장 매칭 모드는 exact, glob, regexp예요.
  • testConstraint constraint ?boolean?: 이름이 constraint인 제약(constraint)과 연관된 boolean 값을 설정하거나 반환해요. 아래 TEST CONSTRAINTS를 보세요.
  • interpreter ?executableName?: configure -singleproc이 false일 때 runAllTests가 각 테스트 파일을 실행하기 위해 exec하는 실행 파일의 이름을 설정하거나 반환해요. interpreter의 기본값은 info nameofexecutable이 반환하는 현재 실행 중인 프로그램의 이름이에요.
  • outputChannel ?channel?: 출력 채널을 설정하거나 반환해요. 기본값은 stdout이에요. 테스트 관련 출력을 내는 테스트는 그 출력을 기본 stdout으로 두지 말고 outputChannel으로 보내야 해요.
  • errorChannel ?channel?: 오류 채널을 설정하거나 반환해요. 기본값은 stderr이에요. 오류 메시지를 출력하는 테스트는 stderr로 직접 출력하지 말고 errorChannel으로 보내야 해요.

단축 구성 명령(Shortcut Configuration Commands)

  • debug ?level?: "configure -debug ?level?"와 같음.
  • errorFile ?filename?: "configure -errfile ?filename?"와 같음.
  • limitConstraints ?boolean?: "configure -limitconstraints ?boolean?"와 같음.
  • loadFile ?filename?: "configure -loadfile ?filename?"와 같음.
  • loadScript ?script?: "configure -load ?script?"와 같음.
  • match ?patternList?: "configure -match ?patternList?"와 같음.
  • matchDirectories ?patternList?: "configure -relateddir ?patternList?"와 같음.
  • matchFiles ?patternList?: "configure -file ?patternList?"와 같음.
  • outputFile ?filename?: "configure -outfile ?filename?"와 같음.
  • preserveCore ?level?: "configure -preservecore ?level?"와 같음.
  • singleProcess ?boolean?: "configure -singleproc ?boolean?"와 같음.
  • skip ?patternList?: "configure -skip ?patternList?"와 같음.
  • skipDirectories ?patternList?: "configure -asidefromdir ?patternList?"와 같음.
  • skipFiles ?patternList?: "configure -notfile ?patternList?"와 같음.
  • temporaryDirectory ?directory?: "configure -tmpdir ?directory?"와 같음.
  • testsDirectory ?directory?: "configure -testdir ?directory?"와 같음.
  • verbose ?level?: "configure -verbose ?level?"와 같음.

기타 명령(Other Commands)

tcltest가 제공하는 남은 명령들은 tcltest나 Tcl 자체가 더 나은 대안을 갖고 있어요. 기존 테스트 스위트를 지원하기 위해 유지되지만 새 코드에서는 피해야 해요.

  • test name description optionList: 이 형태는 test에 여러 줄에 걸친 많은 옵션을, 각 옵션 사이의 줄바꿈을 백슬래시로 인용할 필요 없이 단일 중괄호로 인용된 인자로 넘기기 위해 제공됐어요. optionList 인자는 test에 넘길 option과 value 인자를 나타내는 짝수 요소 리스트일 것으로 기대돼요. 그러나 이 값들은 switch의 다른 형태처럼 직접 전달되지 않아요. 대신 이 형태는 리스트 요소 중 일부에 치환을 수행해 중괄호로 둘러싸인 '블록'의 '내가 의미하는 대로(do what I mean)' 해석을 구현하려는 불행한 시도를 해요. 결과는 거의 문서화하기 불가능해서 이 형태는 권장되지 않아요. 아래 CREATING TEST SUITES WITH TCLTEST의 예제를 보면 이 형태가 백슬래시 인용 줄바꿈을 피하는 데 정말 필요하지 않다는 걸 알 수 있어요.
  • workingDirectory ?directoryName?: 테스트 스위트가 실행 중일 때 현재 작업 디렉터리를 설정하거나 반환해요. workingDirectory의 기본값은 테스트 스위트가 시작된 디렉터리예요. Tcl cdpwd 명령이 충분한 대체물이에요.
  • normalizeMsg msg: msg에서 '추가' 줄바꿈을 제거한 결과를 반환해요. '추가'는 다소 부정확해요. Tcl은 원하는 대로 문자열을 수정할 충분한 문자열 처리 명령을 제공하고, customMatch는 실제·예상 결과의 유연한 매칭을 허용해요.
  • normalizePath pathVar: 경로의 심볼릭 링크를 해석해 내부 리다이렉션이 없는 경로를 만들어요. pathVar는 절대 경로로 가정돼요. pathVar는 제자리에서 수정돼요. Tcl file normalize 명령이 충분한 대체물이에요.

TESTS

test 명령은 tcltest 패키지의 핵심이에요. 기본 기능은 Tcl 스크립트를 평가해 그 결과를 예상 결과와 비교하는 것이에요. test의 옵션은 테스트 스크립트, 평가 환경, 예상 결과, 실제 결과를 예상 결과와 비교하는 방법을 정의해요. tcltest의 일부 구성 옵션도 test의 동작에 영향을 줘요.

test의 유효한 옵션은 다음과 같이 요약돼요.

test name description
        ?-constraints keywordList|expression?
        ?-setup setupScript?
        ?-body testScript?
        ?-cleanup cleanupScript?
        ?-result expectedAnswer?
        ?-output expectedOutput?
        ?-errorOutput expectedError?
        ?-returnCodes codeList?
        ?-errorCode expectedErrorCode?
        ?-match mode?

name은 어떤 문자열이든 될 수 있어요. 다음 패턴에 따라 이름을 고르는 게 관례예요.

target-majorNum.minorNum

화이트박스(회귀) 테스트의 경우 target은 테스트되는 C 함수나 Tcl 프로시저의 이름이어야 해요. 블랙박스 테스트의 경우 target은 테스트되는 기능의 이름이어야 해요. 어떤 관례는 블랙박스 테스트 이름의 접미사를 _bb로 하라고 해요. 관련 테스트는 같은 major 번호를 공유해야 해요. 테스트 스위트가 진화하면서 같은 테스트 이름이 같은 테스트에 계속 대응하는 것이 가장 좋은데, 그래야 'Test foo-1.3은 3.4까지 모든 릴리스에서 통과했는데 3.5 릴리스에서 실패하기 시작했다' 같은 말이 의미 있게 유지돼요.

test 평가 중 nameconfigure -matchconfigure -skip이 반환하는 문자열 매칭 패턴 목록과 비교돼요. 테스트는 nameconfigure -match의 어떤 패턴과도 일치하고 configure -skip의 어떤 패턴과도 일치하지 않을 때만 실행돼요.

description은 테스트의 짧은 텍스트 설명이어야 해요. 설명은 test가 만드는 출력(보통 테스트 실패 메시지)에 포함돼요. 좋은 설명 값은 테스트 스위트 사용자에게 테스트 목적을 간단히 설명해야 해요. 회귀 테스트에는 테스트되는 Tcl/C 함수 이름을 설명에 포함해야 해요. 테스트 케이스가 버그를 재현하기 위해 존재하면 버그 ID를 설명에 포함하세요.

유효한 속성과 값은 다음과 같아요.

  • -constraints keywordList|expression: 선택적 -constraints 속성은 하나 이상의 키워드 목록이거나 표현식일 수 있어요. 값이 키워드 목록이면 각 키워드는 testConstraint 호출로 정의된 제약의 이름이어야 해요. 나열된 제약 중 어떤 것이 false이거나 존재하지 않으면 테스트는 건너뜀(skip)돼요. 값이 표현식이면 그 표현식이 평가돼요. true로 평가되면 테스트가 실행돼요. 표현식 형태의 -constraintsconfigure -constraintsconfigure -limitconstraints의 동작을 방해할 수 있어 권장되지 않아요. 항상 실행되면 안 되는 테스트에는 적절한 제약이 추가되어야 해요. 즉 테스트의 조건부 평가는 test의 조건부 평가가 아니라 -constraints 옵션으로 이뤄져야 해요. 그래야 테스트 스위트가 항상 같은 수의 테스트를 보고하지만, 건너뜀 수는 테스트 환경에 따라 바뀔 수 있어요. 기본값은 빈 목록이에요. 아래 TEST CONSTRAINTS에서 내장 제약 목록과 자신의 제약을 추가하는 방법을 보세요.
  • -setup script: 선택적 -setup 속성은 -body 속성 스크립트 전에 실행될 스크립트를 나타내요. script 평가가 오류를 발생하면 테스트는 실패해요. 기본값은 빈 스크립트예요.
  • -body script: -body 속성은 테스트를 수행하기 위해 실행할 스크립트를 나타내며, 정확성을 검사할 수 있는 결과를 반환해야 해요. script 평가가 오류를 발생하면 테스트는 실패해요(-returnCodes 옵션으로 오류가 예상된다고 명시하지 않는 한). 기본값은 빈 스크립트예요.
  • -cleanup script: 선택적 -cleanup 속성은 -body 스크립트 다음에 실행될 스크립트를 나타내요. script 평가가 오류를 발생하면 테스트는 실패해요. 기본값은 빈 스크립트예요.
  • -match mode: -match 속성은 -result, -output, -errorOutput이 제공하는 예상 답변을 비교하는 방법을 결정해요. mode의 유효한 값은 regexp, glob, exact, 그리고 이전 customMatch 호출이 등록한 어떤 값이든 돼요. 기본값은 exact예요.
  • -result expectedValue: -result 속성은 script의 반환 값을 대조할 expectedValue를 제공해요. 기본값은 빈 문자열이에요.
  • -output expectedValue: -output 속성은 스크립트 평가 중 stdout이나 outputChannel로 보낸 출력을 대조할 expectedValue를 제공해요. 비교에는 전역 puts 명령으로 인쇄한 출력만 사용된다는 점에 주의하세요. -output을 지정하지 않으면 stdout·outputChannel로 보낸 출력은 비교를 위해 처리되지 않아요.
  • -errorOutput expectedValue: -errorOutput 속성은 스크립트 평가 중 stderr이나 errorChannel로 보낸 출력을 대조할 expectedValue를 제공해요. 비교에는 전역 puts로 인쇄한 출력만 사용돼요. -errorOutput을 지정하지 않으면 stderr·errorChannel로 보낸 출력은 비교를 위해 처리되지 않아요.
  • -returnCodes expectedCodeList: 선택적 -returnCodes 속성은 -body 스크립트 평가에서 받아들일 수 있는 반환 코드 목록인 expectedCodeList를 제공해요. -body 스크립트 평가가 expectedCodeList에 없는 코드를 반환하면 테스트는 실패해요. return이 아는 모든 반환 코드(수치·기호 형태, 확장 반환 코드 포함)가 expectedCodeList에서 유효한 요소예요. 기본값은 "ok return"이에요.
  • -errorCode expectedErrorCode: 선택적 -errorCode 속성은 -body 스크립트 평가가 보고한 오류 코드와 일치해야 하는 glob 패턴인 expectedErrorCode를 제공해요. 평가가 expectedErrorCode와 일치하지 않는 코드를 반환하면 테스트는 실패해요. 기본값은 "*"이에요. -returnCodeserror를 포함하지 않으면 error로 설정돼요.

테스트가 통과하려면 -setup, -body, -cleanup 스크립트를 성공적으로 평가해야 해요. -body 스크립트의 반환 코드와 결과는 예상 값과 일치해야 하고, 지정되면 테스트의 출력·오류 데이터는 예상 -output-errorOutput 값과 일치해야 해요. 이 조건 중 하나라도 충족되지 않으면 테스트는 실패해요. 모든 스크립트는 test 호출자의 컨텍스트에서 평가된다는 점에 주의하세요.

test가 모든 속성에 대해 유효한 문법과 합법적인 값으로 호출되는 한 오류를 발생시키지 않아요. 테스트 실패는 대신 outputChannel에 쓰인 출력으로 보고돼요. 기본 동작에서 성공한 테스트는 출력을 만들지 않아요. test가 만드는 출력 메시지는 아래 CONFIGURABLE OPTIONS에서 설명하는 configure -verbose 옵션으로 제어돼요. 테스트 스크립트가 만드는 출력은 outputChannel이나 errorChannel로 보내는 puts로 만들어야 해요. 그래야 테스트 스위트 사용자가 configure -outfileconfigure -errfile 옵션으로 출력을 쉽게 잡을 수 있고, -output-errorOutput 속성이 제대로 작동해요.

TEST CONSTRAINTS(테스트 제약)

제약은 테스트를 건너뛸지 여부를 결정하는 데 쓰여요. 각 제약은 이름(어떤 문자열이든)과 boolean 값을 가져요. 각 테스트는 제약 이름 목록인 -constraints 값을 가져요. 제약 제어에는 두 가지 모드가 있어요. 대부분의 경우 configure -limitconstraints를 false로 설정한 기본 모드가 쓰여요. 목록의 모든 제약이 true 값일 때만 테스트가 실행돼요. 따라서 test-constraints 옵션은 테스트가 가능하거나 의미 있으려면 필요한 조건을 정의하는 편리하고 기호적인 방법이에요. 예를 들어 -constraints unix인 테스트는 제약 unix가 true일 때만(테스트 스위트가 Unix 플랫폼에서 실행 중임을 나타냄) 실행돼요.

각 테스트는 적절한 곳에서만 실행되도록 필요한 -constraints를 포함해야 해요. 아래에 나열된 몇몇 제약이 tcltest 패키지에 미리 정의되어 있어요. 사용자 정의 제약의 등록은 testConstraint 명령으로 수행돼요. 사용자 정의 제약은 테스트 파일 안이나 configure -load 또는 configure -loadfile 옵션이 지정한 스크립트 안에 나타날 수 있어요.

tcltest 패키지가 미리 정의한 제약 목록은 다음과 같아요.

  • singleTestInterp: 모든 테스트 파일이 단일 인터프리터로 source될 때만 실행 가능.
  • unix: 어떤 Unix 플랫폼에서만 실행 가능.
  • win: 어떤 Windows 플랫폼에서만 실행 가능.
  • nt: 어떤 Windows NT 플랫폼에서만 실행 가능.
  • mac: 어떤 Mac 플랫폼에서만 실행 가능.
  • unixOrWin: Unix나 Windows 플랫폼에서만 실행 가능.
  • macOrWin: Mac이나 Windows 플랫폼에서만 실행 가능.
  • macOrUnix: Mac이나 Unix 플랫폼에서만 실행 가능.
  • tempNotWin: Windows에서는 실행 불가. 테스트를 임시로 비활성화하는 데 쓰는 플래그.
  • tempNotMac: Mac에서는 실행 불가. 테스트를 임시로 비활성화하는 데 쓰는 플래그.
  • unixCrash: Unix에서 실행하면 충돌. 테스트를 임시로 비활성화하는 데 쓰는 플래그.
  • winCrash: Windows에서 실행하면 충돌. 테스트를 임시로 비활성화하는 데 쓰는 플래그.
  • macCrash: Mac에서 실행하면 충돌. 테스트를 임시로 비활성화하는 데 쓰는 플래그.
  • emptyTest: 이 테스트는 비어 있어서 실행할 가치가 없지만, 나중에 작성할 테스트의 자리 표시자로 남아 있어요. 사용자가 달리 지정하지 않는 한 테스트를 건너뛰도록 값이 false예요.
  • knownBug: 이 테스트는 실패한다고 알려져 있고 버그가 아직 고쳐지지 않았어요. 사용자가 달리 지정하지 않는 한 건너뛰도록 값이 false예요.
  • nonPortable: 이 테스트는 어떤 알려진 개발 환경에서만 실행될 수 있어요. 일부 테스트는 단어 길이, 파일시스템 구성, 창 관리자 등에 의존하기 때문에 본질적으로 비이식성이에요. 사용자가 달리 지정하지 않는 한 건너뛰도록 값이 false예요.
  • userInteraction: 이 테스트는 사용자의 상호작용이 필요해요. 사용자가 달리 지정하지 않는 한 건너뛰도록 값이 false예요.
  • interactive: 인터프리터가 대화형 모드일 때만(전역 ::tcl_interactive 변수가 1로 설정) 실행 가능.
  • nonBlockFiles: 플랫폼이 파일을 비차단 모드로 설정하는 것을 지원할 때만 실행 가능.
  • asyncPipeClose: 플랫폼이 파이프에 비동기 flush와 비동기 close를 지원할 때만 실행 가능.
  • unixExecs: 이 기계에 Unix 스타일 명령 cat, echo, sh, wc, rm, sleep, fgrep, ps, chmod, mkdir이 있을 때만 실행 가능.
  • hasIsoLocale: ISO 로케일로 전환할 수 있을 때만 실행 가능.
  • root: Unix 사용자가 root일 때만 실행 가능.
  • notRoot: Unix 사용자가 root가 아닐 때만 실행 가능.
  • eformat: 앱에 부동소수점의 "e" 형식에 관해 작동하는 sprintf가 있을 때만 실행 가능.
  • stdio: 인터프리터를 파이프로 열 수 있을 때만 실행 가능.

대안적인 제약 제어 모드는 configure -limitconstraints를 true로 설정해 활성화돼요. 이 구성 설정에서 configure -constraints가 반환하는 제약 목록에 있는 것을 제외한 모든 기존 제약이 false로 설정돼요. configure -constraints의 값이 설정되면 그 모든 제약이 true로 설정돼요. 효과는 configure -constraintsconfigure -limitconstraints를 둘 다 쓰면, configure -constraints 목록의 제약만 포함한 테스트만 실행되고 나머지는 모두 건너뛴다는 것이에요. 예를 들어 알려진 버그를 테스트하는 정확히 그 테스트들을 실행하고 그중 통과하는 게 있는지 발견하기 위해(버그가 고쳐졌음을 나타냄) 이런 구성으로 설정할 수 있어요.

configure -constraints knownBug \
          -limitconstraints true \
          -verbose pass

RUNNING ALL TESTS(모든 테스트 실행)

단일 명령 runAllTests는 여러 파일과 디렉터리에 걸친 전체 테스트 스위트를 실행하기 위해 평가돼요. tcltest의 구성 옵션이 정확한 동작을 제어해요. runAllTests 명령은 먼저 구성 요약을 outputChannel에 출력해요.

평가할 테스트 파일은 configure -testdir 디렉터리에서 찾아져요. 그 디렉터리의 파일 목록 중 configure -file의 어떤 패턴과도 일치하고 configure -notfile의 어떤 패턴과도 일치하지 않는 것이 생성·정렬돼요. 그런 다음 각 파일이 차례로 평가돼요. configure -singleproc이 true면 각 파일이 호출자의 컨텍스트에서 source돼요. false면 interpreter의 복사본이 exec되어 각 파일을 평가해요. 다중 프로세스 동작은 테스트가 프로세스를 종료시킬 만큼 심각한 오류를 일으킬 수 있을 때 유용해요. 그런 오류가 한 파일을 평가하는 자식 프로세스를 종료시켜도 메인 프로세스는 테스트 스위트의 나머지를 계속할 수 있어요. 다중 프로세스 동작에서 메인 프로세스의 tcltest 구성은 configure -outfile을 제외하고 명령줄 인자로 자식 프로세스에 전달돼요. 메인 프로세스의 runAllTests 명령은 자식 프로세스의 모든 출력을 모아 그 결과를 하나의 메인 보고서로 정리해요. 개별 테스트 실패 보고나 configure -verbose 설정이 요청한 메시지는 메인 프로세스가 outputChannel로 직접 전달해요.

선택된 모든 테스트 파일을 평가한 뒤 결과 요약이 outputChannel에 출력돼요. 요약은 평가된 테스트 총수를 건너뜀·통과·실패로 나눠 포함해요. 평가된 파일 수와 실패한 테스트·오류가 있는 파일 이름도 포함해요. 테스트를 건너뛰게 한 제약 목록과 각각에 대해 건너뛴 테스트 수도 출력돼요. 또한 테스트 파일 평가가 configure -tmpdir에 임시 파일을 남긴 것으로 보이면 메시지가 출력돼요.

선택된 모든 테스트 파일을 완료·요약한 뒤 runAllTestsconfigure -testdir의 하위 디렉터리에 재귀적으로 작동해요. configure -relateddir의 어떤 패턴과도 일치하고 configure -asidefromdir의 어떤 패턴과도 일치하지 않는 모든 하위 디렉터리가 검사돼요. 그런 디렉터리에 all.tcl이라는 파일이 있으면 호출자의 컨텍스트에서 source돼요. 검사된 디렉터리에 all.tcl이 있든 없든, 그 하위 디렉터리들도 configure -relateddirconfigure -asidefromdir 패턴에 대해 스캔돼요. 이렇게 해서 디렉터리 트리의 많은 디렉터리가 단일 runAllTests 명령으로 모든 테스트 파일을 평가받을 수 있어요.

CONFIGURABLE OPTIONS(구성 옵션)

configure 명령은 tcltest의 구성 옵션을 설정·조회하는 데 쓰여요. 유효한 옵션은 다음과 같아요.

  • -singleproc boolean: runAllTests가 각 테스트 파일에 대해 자식 프로세스를 생성할지 제어해요. boolean이 true면 생성하지 않아요. 기본값은 false예요.

  • -debug level: 디버그 레벨을 level로 설정해요. stdout에 얼마나 많은 디버그 정보를 출력할지 나타내는 정수예요. 디버그 메시지는 configure -outfile 값과 무관하게 항상 stdout으로 가요. 기본값은 0이에요. 레벨은 다음과 같아요.

    • 0: 디버그 정보를 표시하지 않음.
    • 1: 테스트가 configure -match로 지정된 테스트와 일치하지 않아 건너뜀(userSpecifiedNonMatch) 또는 configure -skip으로 지정된 테스트와 일치해서 건너뜀(userSpecifiedSkip) 여부에 관한 정보를 표시. 테스트 파일의 정리·균형 부족 가능성에 대한 경고와 테스트 이름 재사용에 대한 경고도 출력.
    • 2: 명령줄 프로세서가 파싱한 플래그 배열, 전역 env 배열 내용, 사용될 때 현재 네임스페이스에 존재하는 모든 사용자 정의 변수를 표시.
    • 3: 테스트 하니스의 개별 프로시저가 무엇을 하는지에 관한 정보를 표시.
  • -verbose level: 원하는 출력 상세도를 level로 설정해요. body, pass, skip, start, error, line, msec, usec 요소 중 0개 이상의 목록이에요. 기본값은 "body error"예요. 레벨은 다음과 같아요.

    • body (b): 실패한 테스트의 본문을 표시.
    • pass (p): 테스트가 통과하면 출력.
    • skip (s): 테스트가 건너뛰어지면 출력.
    • start (t): 테스트가 시작될 때마다 출력.
    • error (e): 테스트 반환 코드가 예상 반환 코드와 일치하지 않을 때, 존재하면 errorInfoerrorCode를 출력.
    • line (l): 실패한 테스트의 소스 파일 줄 정보를 출력.
    • msec (m): 각 테스트의 실행 시간을 밀리초로 출력.
    • usec (u): 각 테스트의 실행 시간을 마이크로초로 출력.

    msecusec 상세도 레벨은 표시적 측정으로만 제공된다는 점을 주의하세요. 성능 테스트나 벤치마크에서 고려해야 할 반복성 문제를 다루지 않아요. 이 상세도 레벨로 성능 저하를 철저히 추적하려면 테스트 본문을 time 명령으로 감싸는 것을 고려하세요. 위에서 언급한 단일 문자 약어도 인식되어 "configure -verbose pt""configure -verbose {pass start}"와 같아요.

  • -preservecore level: 코어 보존 레벨을 level로 설정해요. 코어 파일 검사가 얼마나 엄격한지 결정해요. 기본값은 0이에요. 레벨은 다음과 같아요.

    • 0: 검사하지 않음. 각 test 명령 끝에 코어 파일을 검사하지 않지만 모든 테스트 파일 평가 후 runAllTests에서는 검사.
    • 1: 또한 각 test 명령 끝에 코어 파일을 검사.
    • 2: 위의 모든 시점에 코어 파일을 검사하고, 만들어지는 각 코어 파일의 사본을 configure -tmpdir에 저장.
  • -limitconstraints boolean: 위 TESTS에서 설명한 제약을 test가 존중하는 모드를 설정해요. 기본값은 false예요.

  • -constraints list: 목록의 모든 제약을 true로 설정해요. 위 TESTS에서 설명한 대안 제약 모드를 제어하기 위해 configure -limitconstraints true와 함께 쓰이기도 해요. 기본값은 빈 목록이에요.

  • -tmpdir directory: makeFile, makeDirectory, viewFile, removeFile, removeDirectory가 기본 디렉터리로 쓸 임시 디렉터리를 설정해요. 테스트 파일이 만드는 임시 파일·디렉터리가 여기 만들어져야 해요. 기본값은 workingDirectory예요.

  • -testdir directory: runAllTests가 테스트 파일과 하위 디렉터리를 검색하는 디렉터리를 설정해요. 기본값은 workingDirectory예요.

  • -file patternList: runAllTests가 무엇을 평가할 테스트 파일로 결정하는 데 쓰는 패턴 목록을 설정해요. 기본값은 "*.test"예요.

  • -notfile patternList: runAllTests가 무엇을 건너뛸 테스트 파일로 결정하는 데 쓰는 패턴 목록을 설정해요. 기본값은 "l.*.test"로, SCCS 잠금 파일이 건너뛰어지게 해요.

  • -relateddir patternList: runAllTestsall.tcl 파일을 검색할 하위 디렉터리를 결정하는 데 쓰는 패턴 목록을 설정해요. 기본값은 "*"이에요.

  • -asidefromdir patternList: runAllTestsall.tcl 파일 검색 시 건너뛸 하위 디렉터리를 결정하는 데 쓰는 패턴 목록을 설정해요. 기본값은 빈 목록이에요.

  • -match patternList: test가 테스트를 실행할지 결정하는 데 쓰는 패턴 목록을 설정해요. 기본값은 "*"이에요.

  • -skip patternList: test가 테스트를 건너뛸지 결정하는 데 쓰는 패턴 목록을 설정해요. 기본값은 빈 목록이에요.

  • -load script: loadTestedCommands가 평가할 스크립트를 설정해요. 기본값은 빈 스크립트예요.

  • -loadfile filename: loadTestedCommands가 평가할 스크립트를 읽을 파일 이름을 설정해요. -load의 대안이에요. 함께 쓸 수 없어요.

  • -outfile filename: tcltest가 만드는 모든 출력을 쓸 파일을 설정해요. filename이라는 파일이 쓰기용으로 열리고 결과 채널이 outputChannel의 값으로 설정돼요.

  • -errfile filename: tcltest가 만드는 모든 오류 출력을 쓸 파일을 설정해요. filename이라는 파일이 쓰기용으로 열리고 결과 채널이 errorChannel의 값으로 설정돼요.

타입으로 테스트 스위트 만들기(CREATING TEST SUITES WITH TCLTEST)

테스트 스위트의 근본 요소는 개별 test 명령이에요. 몇 가지 예제부터 시작해 볼게요.

정상적으로 반환하는 스크립트의 테스트.

test example-1.0 {normal return} {
    format %s value
} value

컨텍스트 설정·정리가 필요한 스크립트의 테스트. 줄 연속을 피하는 중괄호·들여쓰기 스타일에 주목하세요.

test example-1.1 {test file existence} -setup {
    set file [makeFile {} test]
} -body {
    file exists $file
} -cleanup {
    removeFile test
} -result 1

오류를 발생시키는 스크립트의 테스트.

test example-1.2 {error return} -body {
    error message
} -returnCodes error -result message

제약이 있는 테스트.

test example-1.3 {user owns created files} -constraints {
    unix
} -setup {
    set file [makeFile {} test]
} -body {
    file attributes $file -owner
} -cleanup {
    removeFile test
} -result $::tcl_platform(user)

다음으로 더 높은 조직 계층에서는 여러 test 명령이 단일 테스트 파일로 모여요. 테스트 파일은 runAllTests가 테스트 파일을 찾는 데 쓰는 기본 패턴이므로 .test 확장자를 가져야 해요. 프로젝트의 각 소스 코드 파일마다 테스트 파일 하나를 두는 것이 좋은 경험칙이에요. 테스트 파일과 소스 코드 파일을 함께 편집해 테스트를 코드 변경과 동기화하는 게 좋은 습관이에요.

테스트 파일의 대부분 코드는 test 명령이어야 해요. test의 조건부 평가가 아니라 제약으로 테스트를 건너뛰세요.

제약으로 보호하는 조건부 테스트 작성 권장법:

testConstraint X [expr $myRequirement]
test goodConditionalTest {} X {
    # body
} result

if로 보호하는 조건부 테스트 작성 비권장법:

if $myRequirement {
    test badConditionalTest {} {
        #body
    } result
}

-setup-cleanup 옵션으로 테스트 본문의 모든 컨텍스트 요구를 설정·해제하세요. 테스트가 파일의 이전 테스트에 의존하게 만들지 마세요. 그 이전 테스트는 건너뛸 수 있어요. 여러 연속 테스트가 같은 컨텍스트를 요구하면 적절한 setup·cleanup 스크립트를 변수에 저장해 각 테스트의 -setup, -cleanup 옵션에 넘길 수 있어요. 이는 test 명령 밖에서 setup을 수행하는 것보다 나은데, setup이 필요할 때만 수행되고 setup 중 오류가 보고되며 테스트 파일을 중단시키지 않기 때문이에요.

테스트 파일은 다른 테스트 파일과 결합되어도, 심지어 configure -singleproc 1이 모든 파일을 공통 인터프리터에서 평가하게 해도 방해하지 않아야 해요. 이를 달성하는 간단한 방법은 테스트가 모든 명령과 변수를 테스트 파일 평가가 끝나면 삭제되는 네임스페이스에 정의하게 하는 것이에요. 좋은 네임스페이스는 테스트하는 모듈 네임스페이스의 자식 네임스페이스 test예요.

테스트 파일은 또한 메인 runAllTests가 호출하는 것에 의존하지 않고 스크립트로 직접 평가될 수 있어야 해요. 이는 각 테스트 파일이 tcltest가 제공하는 모든 구성 제어를 시험자에게 주도록 명령줄 인자를 처리해야 한다는 뜻이에요.

테스트 파일의 모든 테스트 뒤에는 cleanupTests 명령이 호출되어야 해요.

이 점들을 보여주는 샘플 테스트 파일 스케치는 다음과 같아요.

package require tcltest 2.5
::tcltest::configure {*}$::argv
package require example
namespace eval ::example::test {
    namespace import ::tcltest::*
    testConstraint X [expr {...}]
    variable SETUP {#common setup code}
    variable CLEANUP {#common cleanup code}
    test example-1 {} -setup $SETUP -body {
        # First test
    } -cleanup $CLEANUP -result {...}
    test example-2 {} -constraints X -setup $SETUP -body {
        # Second test; constrained
    } -cleanup $CLEANUP -result {...}
    test example-3 {} {
        # Third test; no context required
    } {...}
    cleanupTests
}
namespace delete ::example::test

다음 조직 계층은 여러 테스트 파일로 이뤄진 전체 테스트 스위트예요. 전체 스위트를 제어하는 데 한 스크립트가 쓰여요. 이 스크립트의 기본 기능은 필요한 설정을 한 뒤 runAllTests를 호출하는 것이에요. 여러 테스트 스위트를 한 테스트 실행으로 결합할 때 runAllTests가 쓰는 기본 이름이므로 보통 all.tcl이라는 이름을 써요.

샘플 테스트 스위트 메인 스크립트 스케치:

package require tcltest 2.5
package require example
::tcltest::configure -testdir \
        [file dirname [file normalize [info script]]]
::tcltest::configure {*}$::argv
::tcltest::runAllTests

호환성(Compatibility)

이전 릴리스의 tcltest가 제공하는 ::tcltest 네임스페이스의 많은 명령과 변수는 여기서 문서화되지 않았어요. 그들은 더 이상 tcltest의 지원되는 공개 인터페이스의 일부가 아니며 새 테스트 스위트에서 쓰면 안 돼요. 그러나 옛 인터페이스 명세로 작성된 기존 테스트 스위트를 계속 지원하기 위해, 그중 많은 비권장 명령·변수는 여전히 예전처럼 작동해요. 예를 들어 많은 상황에서 package require tcltest 2.1이 성공한 직후 ::argv 변수의 인자로 configure가 자동 호출돼요. 이는 tcltest가 명령줄 인자로 자동 구성되던 옛 동작에 의존하는 테스트 스위트를 지원하기 위한 것이에요. 새 테스트 파일은 이것에 의존하지 말고, 명령줄 인자로 구성하기 위해 명시적으로 다음을 포함해야 해요.

::tcltest::configure {*}$::argv

알려진 문제(Known Issues)

test의 중첩 평가와 관련된 두 가지 알려진 문제가 있어요. 첫 번째는 테스트 스크립트가 실행되는 스택 레벨과 관련돼요. 다른 테스트 안에 중첩된 테스트는 가장 바깥쪽 테스트와 같은 스택 레벨에서 실행될 수 있어요. 예를 들어 다음 코드에서

test level-1.1 {level 1} {
    -body {
        test level-2.1 {level 2} {
        }
    }
}

level-2.1에서 실행되는 어떤 스크립트든 level-1.1에 대해 정의된 스크립트와 같은 스택 레벨에서 실행될 수 있어요. 또한 두 테스트가 실행됐지만 결과는 level-1.1과 같은 레벨의 테스트에 대해서만 cleanupTests가 보고해요. 그러나 level-1.1 이전에 실행된 모든 테스트의 결과는 level-2.1이 실행될 때 사용 가능해요. 이는 level-2.1의 테스트 결과에 접근하려 하면 level-1.1과 같은 테스트 레벨에서 실행된 테스트를 가리키는 "m"개의 테스트가 실행됐고 "n"개의 테스트가 건너뛰고 "o"개가 통과하고 "p"개가 실패했다고 말할 수 있다는 뜻이에요.

test 명령의 출력·오류 비교 구현은 애플리케이션 코드에서 puts를 사용하는 것에 의존해요. 출력은 정의된 테스트 스크립트가 실행되는 동안 전역 puts 명령을 재정의해 가로채요. C 프로시저가 발생시킨 오류나 C 애플리케이션에서 직접 출력한 것은 test 명령이 잡지 못해요. 따라서 -output-errorOutput 옵션 사용은 puts로 출력을 만드는 순수 Tcl 애플리케이션에만 유용해요.

더 알아보기

  • test — 테스트 정의·실행
  • tclsh — Tcl 셸