Qt Test 모범 사례
버그 수정 및 신규 기능 추가 시 Qt Test를 추가할 것을 권장합니다. 버그를 수정하기 전에, 수정 전에는 실패하여 버그를 재현하고 수정 후에는 통과하는 회귀 테스트 (가급적 자동화된 테스트 ) 를 추가하십시오. 신규 기능을 개발하는 동안에는 해당 기능이 의도한 대로 작동하는지 확인하기 위한 테스트를 추가하십시오.
일련의 코딩 표준을 준수하면 모든 환경에서 Qt Test 자동 테스트가 안정적으로 작동할 가능성이 높아집니다. 예를 들어, 일부 테스트는 디스크에서 데이터를 읽어야 합니다. 이를 수행하는 방법에 대한 표준이 설정되어 있지 않으면, 일부 테스트는 이식성이 떨어질 수 있습니다. 예를 들어, 테스트 데이터 파일이 현재 작업 디렉터리에 있다고 가정하는 테스트는 소스 내 빌드에서만 작동합니다. 섀도 빌드(소스 디렉터리 외부)에서는 테스트가 데이터를 찾지 못해 실패하게 됩니다.
다음 섹션에는 Qt Test 작성에 대한 지침이 포함되어 있습니다:
이러한 모범 사례와 함께 ‘보안 고려 사항 ’에 대한 조언도 염두에 두어야 합니다.
일반 원칙
다음 섹션에서는 단위 테스트 작성에 대한 일반적인 지침을 제공합니다:
- 테스트 검증
- 테스트 함수에 설명적인 이름을 지정하십시오
- 자립적인 테스트 함수 작성
- 외부 의존성을 피하십시오
- 전체 스택 테스트하기
- 테스트를 신속하게 완료하세요
- 데이터 주도 테스트를 사용하십시오
- 커버리지 도구 활용
- 테스트를 제외할 적절한 메커니즘을 선택하십시오
- Q_ASSERT 사용 피하기
테스트 검증하기
수정 사항이나 새로운 기능과 함께 테스트를 작성하고 새로운 브랜치에 커밋하십시오. 작업이 완료되면, 작업의 기반이 되는 브랜치를 체크아웃한 다음, 새로운 테스트에 대한 테스트 파일을 이 브랜치로 체크인하세요. 이를 통해 이전 브랜치에서 테스트가 실제로 실패하는지 확인할 수 있으며, 따라서 테스트가 실제로 버그를 포착하거나 새로운 기능을 테스트하는지 확인할 수 있습니다.
예를 들어, Git 버전 관리 시스템을 사용하는 경우 QDateTime 클래스의 버그를 수정하는 워크플로는 다음과 같을 수 있습니다:
- 수정 및 테스트를 위한 브랜치를 생성합니다:
git checkout -b fix-branch dev - 테스트를 작성하고 버그를 수정합니다.
- 수정 사항과 새로운 테스트를 모두 포함하여 빌드하고 테스트하여, 수정 후 새로운 테스트가 통과되는지 확인합니다.
- 수정 사항과 테스트를 브랜치에 추가합니다:
git add tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp src/corelib/time/qdatetime.cpp - 수정 사항과 테스트를 브랜치에 커밋합니다:
git commit -m 'Fix bug in QDateTime' - 수정 사항이 필요한 문제를 테스트가 실제로 감지하는지 확인하려면, 자신의 브랜치를 기반으로 한 브랜치를 체크아웃하세요:
git checkout dev - 수정 브랜치에서 테스트 파일만 체크아웃하세요:
git checkout fix-branch -- tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp소스 트리의 나머지 부분은 수정 사항이 적용되지 않은 상태로 dev 브랜치에 남아 있지만, 해당 브랜치에서 새로운 테스트를 실행해 볼 준비가 되어 있습니다.
- 빌드하고 테스트를 실행하여 dev 브랜치에서 테스트가 실패하는지, 즉 실제로 버그를 포착하는지 확인하십시오.
- 이제 수정 브랜치로 돌아갈 수 있습니다:
git checkout fix-branch - 또는 dev 브랜치에서 작업 트리를 깨끗한 상태로 복원할 수도 있습니다:
git checkout HEAD -- tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp
변경 사항을 검토할 때, 이 워크플로를 활용하여 해당 변경 사항이 실제로 해결하는 문제에 대한 테스트가 포함되어 있는지 확인할 수 있습니다.
테스트 함수에 설명적인 이름을 지정하세요
테스트 케이스의 이름 지정은 중요합니다. 테스트 이름은 테스트 실행 시 실패 보고서에 표시됩니다. 데이터 기반 테스트의 경우, 데이터 행의 이름도 실패 보고서에 표시됩니다. 적절한 이름을 지정하면 보고서를 읽는 사람이 무엇이 잘못되었는지 한눈에 파악할 수 있습니다.
테스트 함수 이름은 해당 함수가 무엇을 테스트하려는 것인지 명확하게 드러내야 합니다. 단순히 버그 추적 식별자를 사용해서는 안 됩니다. 버그 추적 시스템이 교체되면 식별자가 더 이상 유효하지 않게 되기 때문입니다. 또한, 오프라인에서 작업하는 개발자는 버그 추적 시스템에 접근할 수 없으며, 일부 버그 추적 시스템은 모든 사용자가 이용할 수 없는 경우도 있습니다. 버그 보고서가 나중에 테스트 코드를 읽는 사람들에게 유용할 수 있다면, 테스트의 관련 부분 옆에 주석으로 이를 언급할 수 있습니다.
마찬가지로, 데이터 주도 테스트를 작성할 때는 각 테스트 케이스가 기능의 어떤 측면에 초점을 맞추고 있는지 나타내는 설명적인 이름을 지정하십시오. 단순히 테스트 케이스에 번호를 매기거나 버그 추적 식별자를 사용하지 마십시오. 테스트 결과를 읽는 사람은 그 번호나 식별자가 무엇을 의미하는지 전혀 알 수 없을 것입니다. 필요한 경우, 테스트 행에 버그 추적 식별자를 언급하는 주석을 추가할 수 있습니다. 공백 문자와 테스트를 실행하려는 명령줄 셸에서 의미 있는 문자는 피하는 것이 가장 좋습니다. 이렇게 하면 테스트 프로그램에 대한 명령줄에서 테스트와 태그를 더 쉽게 지정할 수 있습니다. 예를 들어, 테스트 실행을 단 하나의 테스트 케이스로만 제한하는 경우 등이 있습니다.
자립적인 테스트 함수 작성
테스트 프로그램 내에서 각 테스트 함수는 서로 독립적이어야 하며, 이전에 실행된 테스트 함수에 의존해서는 안 됩니다. tst_foo testname 를 사용하여 테스트 함수를 단독으로 실행해 보면 이를 확인할 수 있습니다. 데이터 기반 테스트의 경우에도 마찬가지로, 테스트 데이터 테이블의 행 간 의존성을 피해야 합니다. 그래야 tst_foo function:tag 를 사용하여 단일 행을 독립적으로 실행할 수 있습니다(예를 들어, 실패 원인을 조사하기 위해).
여러 테스트에서 테스트 대상 클래스의 인스턴스를 재사용하지 마십시오. 테스트 인스턴스(예: 위젯)는 테스트의 멤버 변수가 되어서는 안 되며, 테스트가 실패하더라도 적절한 정리 처리가 이루어지도록 스택에서 인스턴스화하는 것이 바람직합니다. 이를 통해 테스트 간 간섭을 방지할 수 있습니다.
테스트에서 전역적인 변경을 수행하는 경우, 테스트가 통과하든 실패하든 상관없이 테스트 종료 시 이전 상태가 복원되도록 주의하십시오. 테스트 실패 시 실패한 검사 이후의 코드가 실행되지 않으므로, 테스트가 실패할 경우 테스트 종료 시 복원하는 방식은 작동하지 않습니다. 실패 시에도 상태를 복원하는 안정적인 방법은, 소멸자가 이전 상태를 복원하는 RAII(자원 획득은 초기화다) 객체를 인스턴스화하는 것입니다. 이는 종종 ` qScopeGuard()`를 사용하여 편리하게 구현할 수 있습니다. 예를 들어
const auto restoreDefaultLocale = qScopeGuard([prior = QLocale()]() {
QLocale::setDefault(prior);
});테스트 대상 코드가 사용하는 로케일을 제어해야 하는 테스트에서 QLocale::setDefault()을 처음 호출하기 전에 다음과 같이 구현할 수 있습니다.
외부 의존성 피하기
테스트 함수 역시 외부 리소스에 대한 의존을 피해야 합니다. 이러한 의존성은 해당 리소스를 일시적으로 사용할 수 없게 될 경우 테스트를 실패로 이끌기 쉽습니다. 리소스를 사용할 수 있는 상황이라 하더라도, 리소스 측에서 테스트 시스템의 접근을 차단할 수 있습니다. 예를 들어, 테스트가 빈번하게 실행될 경우 리소스 측에서는 테스트를 서비스에 대한 원치 않는 부담으로 해석할 수 있습니다.
접근할 수 없을 때 테스트를 건너뛰는 것은, 테스트가 실행되었더라면 드러났을 문제를 숨길 수 있으므로 이러한 문제에 대한 적절한 해결책이 되지 못합니다. 테스트 목적에 필요한 특성을 충분히 갖춘 리소스의 로컬 모사본을 구축하는 것이 더 좋습니다.
외부 종속성은 오프라인에서 작업하는 개발자에게도 문제를 일으킬 뿐만 아니라, 신뢰할 수 없는 출처의 코드 변경 사항을 평가할 때 격리된 가상 머신과 같은 샌드박스 내에서 테스트할 때도 문제를 야기합니다. 관련 우려 사항에 대해서는 Qt Test Security Considerations를 참조하십시오.
전체 스택 테스트
API가 중량급 작업을 수행하는 플러그인 방식이거나 플랫폼별 백엔드를 기반으로 구현된 경우, 백엔드까지 이어지는 모든 코드 경로를 포괄하는 테스트를 작성해야 합니다. 모의 백엔드를 사용하여 상위 계층 API 부분을 테스트하는 것은 API 계층의 오류를 백엔드에서 분리하는 좋은 방법이지만, 실제 환경을 충실히 반영하는 데이터로 실제 구현을 실행하는 테스트를 보완하는 역할에 그칩니다.
테스트를 신속하게 완료하세요
테스트는 불필요한 반복, 부적절하게 방대한 양의 테스트 데이터 사용, 또는 불필요한 대기 시간 발생으로 시간을 낭비해서는 안 됩니다.
이는 특히 단위 테스트의 경우 더욱 그러하며, 단위 테스트 실행 시간이 1초만 더 길어져도 여러 대상에 걸친 브랜치의 CI 테스트 시간이 더 오래 걸리게 됩니다. 단위 테스트는 더 많은 양의 테스트 데이터와 더 긴 테스트 실행 시간이 예상되는 부하 및 신뢰성 테스트와는 별개라는 점을 기억하십시오.
일반적으로 동일한 테스트를 여러 번 실행하는 벤치마크 테스트는 별도의 ` tests/benchmarks ` 디렉터리에 배치해야 하며, 기능 단위 테스트와 혼합되어서는 안 됩니다.
데이터 기반 테스트 활용
데이터 주도 테스트를 사용하면 이후 버그 보고서를 통해 발견된 경계 조건에 대한 새로운 테스트를 더 쉽게 추가할 수 있습니다.
테스트에서 여러 항목을 순차적으로 테스트하는 대신 데이터 주도형 테스트를 사용하면 매우 유사한 코드의 반복을 피할 수 있으며, 앞선 테스트가 실패하더라도 후속 케이스가 확실히 테스트되도록 보장합니다. 또한 각 데이터 샘플에 동일한 테스트가 적용되므로 체계적이고 일관된 테스트를 촉진합니다.
테스트가 데이터 기반인 경우, 테스트 함수 이름과 함께 데이터 태그를 function:tag 와 같이 지정하여, 해당 함수의 모든 테스트 케이스가 아닌 특정 테스트 케이스 하나에만 테스트를 실행할 수 있습니다. 이는 전역 데이터 태그나 함수 자체 데이터의 행을 식별하는 로컬 태그 모두에 사용할 수 있으며, function:global:local 와 같이 두 가지를 결합하여 사용할 수도 있습니다.
커버리지 도구 사용
Coco나 gcov와 같은 커버리지 도구를 사용하여, 테스트 대상 함수나 클래스 내의 가능한 한 많은 문, 분기, 조건을 포괄하는 테스트를 작성하세요. 신규 기능의 개발 주기 초기에 이를 수행할수록, 나중에 코드를 리팩토링할 때 회귀 결함을 더 쉽게 포착할 수 있습니다.
테스트를 제외할 적절한 메커니즘 선택
적용 불가능한 테스트를 제외하기 위해 적절한 메커니즘을 선택하는 것이 중요합니다.
QSKIP()를 사용하여 런타임에 전체 테스트 함수가 현재 테스트 환경에서 적용 불가능한 것으로 판명된 경우를 처리하십시오. 테스트 함수의 일부만 건너뛰어야 하는 경우, 조건문을 사용할 수 있으며, 선택적으로 qDebug() 호출을 통해 적용 불가능한 부분을 건너뛰는 이유를 보고할 수 있습니다.
나중에 수정되어야 할 알려진 테스트 실패가 있는 경우, 가능한 경우 테스트의 나머지 부분을 실행할 수 있도록 지원하는 ` QEXPECT_FAIL `를 사용하는 것이 좋습니다. 또한 이 방법은 문제가 여전히 존재하는지 확인하고, 코드 유지보수자가 의도치 않게 문제를 해결한 경우 이를 알릴 수 있게 해줍니다. 이는 ` Abort ` 플래그를 사용할 때도 얻을 수 있는 이점입니다.
#if 를 사용하면 테스트 함수나 데이터 기반 테스트의 데이터 행을 특정 플랫폼으로 제한하거나, 특정 기능이 활성화된 경우로 제한할 수 있습니다. 그러나 #if 를 사용하여 테스트 함수를 건너뛸 때는 moc의 제한 사항에 주의해야 합니다. moc 전처리기는 컴파일러의 기능 감지에 자주 사용되는 builtin 매크로 중 일부에 접근할 수 없습니다. 따라서 moc 는 전처리기 조건에 대해 코드의 나머지 부분에서 확인되는 결과와 다른 결과를 반환할 수 있습니다. 이로 인해 moc 이 실제 컴파일러가 건너뛰는 테스트 슬롯에 대한 메타데이터를 생성하거나, 실제로 클래스에 컴파일된 테스트 슬롯에 대한 메타데이터를 생략하는 결과가 발생할 수 있습니다. 첫 번째 경우, 테스트는 구현되지 않은 슬롯을 실행하려고 시도할 것입니다. 두 번째 경우, 테스트는 실행되어야 함에도 불구하고 테스트 슬롯을 실행하려고 시도하지 않을 것입니다.
특정 플랫폼에서 전체 테스트 프로그램이 적용되지 않거나 특정 기능이 활성화되지 않은 경우, 테스트 빌드를 방지하기 위해 상위 디렉터리의 빌드 구성을 사용하는 것이 가장 좋은 방법입니다. 예를 들어, tests/auto/gui/someclass 테스트가 macOS에서 유효하지 않은 경우, tests/auto/gui/CMakeLists.txt 내의 하위 디렉터리로 포함된 해당 테스트를 플랫폼 확인 조건으로 감싸주십시오:
if(NOT APPLE)
add_subdirectory(someclass)
endif또는 qmake 를 사용하는 경우, tests/auto/gui.pro 파일에 다음 줄을 추가합니다:
mac*: SUBDIRS -= someclassQSKIP을 사용하여 테스트 건너뛰기도 참조하십시오.
Q_ASSERT 사용은 피하십시오
Q_ASSERT 매크로는 false 에서 명시된 조건이 충족될 때마다 프로그램을 중단시키지만, 이는 소프트웨어가 디버그 모드로 빌드된 경우에만 적용됩니다. 릴리스 빌드와 디버그 및 릴리스 빌드 모두에서 Q_ASSERT 는 아무런 동작도 하지 않습니다.
Q_ASSERT 는 디버그 빌드 테스트 여부에 따라 테스트 동작이 달라지게 하고, 테스트를 즉시 중단시켜 나머지 모든 테스트 함수를 건너뛰며 불완전하거나 형식이 잘못된 테스트 결과를 반환하기 때문에 사용을 피해야 합니다.
또한 테스트 종료 시 수행되어야 할 모든 정리(tear-down)나 정리 작업(tidy-up)을 건너뛰게 되므로, 작업 공간을 정리되지 않은 상태로 남겨둘 수 있으며, 이는 후속 테스트에 문제를 일으킬 수 있습니다.
Q_ASSERT 대신 QCOMPARE() 또는 QVERIFY() 매크로 변형을 사용해야 합니다. 이 매크로들은 현재 테스트가 실패로 보고되고 종료되도록 하지만, 나머지 테스트 함수는 실행되고 전체 테스트 프로그램은 정상적으로 종료되도록 합니다. QVERIFY2()는 테스트 로그에 설명적인 오류 메시지를 기록할 수도 있습니다.
신뢰할 수 있는 테스트 작성
다음 섹션에서는 신뢰할 수 있는 테스트를 작성하기 위한 지침을 제공합니다.
검증 단계에서 부수 효과 피하기
QCOMPARE(), QVERIFY() 등을 사용하여 자동 테스트에서 검증 단계를 수행할 때는 부수 효과를 피해야 합니다. 검증 단계에서 발생하는 부수 효과는 테스트를 이해하기 어렵게 만들 수 있습니다. 또한, 테스트를 QTRY_VERIFY(), QTRY_COMPARE() 또는 QBENCHMARK 를 사용하도록 변경할 때 진단하기 어려운 방식으로 테스트가 쉽게 실패할 수 있습니다. 이러한 함수는 전달된 표현식을 반복적으로 실행할 수 있으므로, 부수 효과도 반복될 수 있습니다.
부작용이 불가피한 경우, 테스트가 실패하더라도 테스트 함수 끝에서 이전 상태가 복원되도록 해야 합니다. 이를 위해서는 일반적으로 RAII 클래스(위의 ‘자립형 테스트 함수 작성’ 참조)나 cleanup() 메서드를 사용해야 합니다. 단순히 테스트 끝부분에 복원 코드를 배치해서는 안 됩니다. 테스트의 일부가 실패하면 해당 코드가 건너뛰어지며, 이전 상태가 복원되지 않습니다.
고정 타임아웃은 피하십시오
QTest::qWait()과 같이 특정 조건이 참이 될 때까지 기다리기 위해 하드코딩된 타임아웃을 사용하지 마십시오. QSignalSpy 클래스, QTRY_VERIFY() 또는 QTRY_COMPARE() 매크로, 혹은 QSignalSpy 클래스를 QTRY_ 매크로 변형과 함께 사용하는 것을 고려하십시오.
qWait() 함수를 사용하면 특정 작업을 수행한 후, 해당 작업에 의해 트리거된 비동기 동작이 완료될 때까지 고정된 기간 동안 지연을 설정할 수 있습니다. 예를 들어, 위젯의 상태를 변경한 다음 위젯이 다시 그려질 때까지 기다리는 경우입니다. 그러나 워크스테이션에서 작성된 테스트를 실제 기기에서 실행할 때, 예상되는 동작이 완료되는 데 더 오랜 시간이 걸릴 수 있어 이러한 타임아웃으로 인해 종종 오류가 발생합니다. 가장 느린 테스트 플랫폼에서 필요한 값보다 몇 배 더 큰 값으로 고정 타임아웃을 늘리는 것은 좋은 해결책이 아닙니다. 이는 모든 플랫폼, 특히 테이블 기반 테스트에서 테스트 실행 속도를 저하시키기 때문입니다.
테스트 대상 코드가 비동기 동작 완료 시 Qt 신호를 발송하는 경우, QSignalSpy 클래스를 사용하여 검증 단계를 수행할 수 있게 되었음을 테스트 함수에 알리는 것이 더 나은 접근 방식입니다.
Qt 신호가 없는 경우, ` QTRY_COMPARE() ` 및 ` QTRY_VERIFY() ` 매크로를 사용하십시오. 이 매크로들은 지정된 조건이 참이 되거나 최대 타임아웃에 도달할 때까지 주기적으로 해당 조건을 테스트합니다. 이러한 매크로를 사용하면 테스트가 필요 이상으로 오래 걸리는 것을 방지할 수 있을 뿐만 아니라, 더 빠른 시스템에서 테스트를 개발하고 나중에 더 느린 시스템에서 실행할 때 발생하는 오류를 피할 수 있습니다.
Qt 신호가 없고, 새로운 API 개발의 일환으로 테스트를 작성하고 있다면, 비동기 동작의 완료를 알리는 신호를 추가하는 것이 API에 도움이 될지 고려해 보십시오. 테스트를 더 쉽게 수행할 수 있게 된다면, API 호출자에게도 유용할 것입니다.
시간에 의존하는 동작에 주의하십시오
일부 테스트 전략은 특정 클래스의 타이밍 의존적 동작에 취약할 수 있으며, 이로 인해 특정 플랫폼에서만 테스트가 실패하거나 일관된 결과를 반환하지 못하는 경우가 발생할 수 있습니다.
한 가지 예로 텍스트 입력 위젯을 들 수 있는데, 이 위젯에는 종종 깜빡이는 커서가 있어 비트맵을 캡처할 때 커서의 상태에 따라 캡처된 비트맵의 비교 결과가 성공하거나 실패할 수 있습니다. 이는 다시 테스트를 실행하는 시스템의 속도에 따라 달라질 수 있습니다.
타이머 이벤트에 따라 상태가 변경되는 클래스를 테스트할 때는 검증 단계를 수행할 때 타이머 기반 동작을 고려해야 합니다. 타이밍에 의존하는 동작은 매우 다양하기 때문에, 이 테스트 문제에 대한 단일한 범용 해결책은 없습니다.
텍스트 입력 위젯의 경우, 가능한 해결책으로는 커서 깜빡임 동작을 비활성화하는 것(API에서 해당 기능을 제공하는 경우), 비트맵을 캡처하기 전에 커서가 알려진 상태가 될 때까지 기다리는 것 (예: API에서 제공하는 경우 적절한 신호를 구독), 또는 비트맵 비교에서 커서가 포함된 영역을 제외하는 방법 등이 있습니다.
비트맵 캡처 및 비교 피하기
비트맵을 캡처하고 비교하여 테스트 결과를 검증하는 것이 때로는 필요하지만, 이는 상당히 불안정하고 많은 노력이 소요될 수 있습니다.
예를 들어, 특정 위젯은 플랫폼이나 위젯 스타일에 따라 모양이 다를 수 있으므로, Qt가 지원하는 플랫폼이 발전함에 따라 참조 비트맵을 여러 번 생성한 다음 지속적으로 관리해야 할 수도 있습니다. 따라서 비트맵에 영향을 미치는 변경을 수행하려면 지원되는 각 플랫폼에서 예상되는 비트맵을 다시 생성해야 하며, 이를 위해서는 각 플랫폼에 대한 접근 권한이 필요합니다.
비트맵 비교는 테스트 기기의 화면 해상도, 비트 심도, 활성화된 테마, 색상 구성, 위젯 스타일, 활성화된 로케일(통화 기호, 텍스트 방향 등), 글꼴 크기, 투명도 효과, 창 관리자 선택과 같은 요인의 영향을 받을 수도 있습니다.
가능한 경우, 비트맵을 캡처하고 비교하는 대신 객체와 변수의 속성을 확인하는 등 프로그래밍적인 방법을 사용하십시오.
테스트 출력 개선
다음 섹션에서는 가독성이 높고 유용한 테스트 출력을 생성하기 위한 지침을 제공합니다.
경고 확인
소프트웨어를 빌드할 때와 마찬가지로, 테스트 출력이 경고로 어수선해지면 실제로 버그 발생의 단서가 되는 경고를 알아차리기 어려워집니다. 따라서 테스트 로그에 경고나 기타 불필요한 출력이 있는지 정기적으로 확인하고 원인을 조사하는 것이 현명합니다. 경고가 버그의 징후일 경우, 경고가 발생하면 테스트가 실패하도록 설정할 수 있습니다.
테스트 대상 코드가 잘못된 사용에 대한 경고와 같은 메시지를 생성해야 하는 경우, 해당 코드가 실제로 그렇게 사용되었을 때 메시지를 생성하는지 테스트하는 것도 중요합니다. ` qWarning()`, ` qDebug()`, ` qInfo()` 등의 함수를 통해 테스트 대상 코드에서 예상되는 메시지가 출력되는지 ` QTest::ignoreMessage()`를 사용하여 테스트할 수 있습니다. 이렇게 하면 메시지가 제대로 출력되는지 확인하고, 테스트 실행 결과에서 해당 메시지를 필터링해 제거할 수 있습니다. 메시지가 출력되지 않으면 테스트는 실패합니다.
예상되는 메시지가 Qt가 디버그 모드로 빌드되었을 때만 출력되는 경우, QLibraryInfo::isDebugBuild()를 사용하여 Qt 라이브러리가 디버그 모드로 빌드되었는지 확인하십시오. #ifdef QT_DEBUG 만으로는 충분하지 않습니다. 이 함수는 테스트가 디버그 모드로 빌드되었는지만 알려줄 뿐, Qt 라이브러리도 디버그 모드로 빌드되었음을 보장하지는 않기 때문입니다.
테스트에서 QTest::failOnWarning()를 호출하여 qWarning() 호출이 발생하지 않는지 확인할 수 있습니다. 매개변수를 지정하지 않을 경우(Qt 6.8부터), 경고가 발생하면 테스트가 실패하며 해당 경고가 출력됩니다. 선택적으로 failOnWarning() 에 테스트할 경고 메시지나 일치시킬 QRegularExpression 을 전달하여, 이 동작을 일치하는 경고로만 제한할 수 있습니다. (이러한 필터링된 버전은 Qt 6.3에서 도입되었습니다.)
또한 환경 변수 ` QT_FATAL_WARNINGS `을 설정하여 경고를 치명적인 오류로 처리하도록 할 수 있습니다. 자세한 내용은 qWarning()을 참조하십시오. 이는 자동 테스트에만 국한된 기능은 아닙니다. 방대한 테스트 로그 속에서 경고가 묻혀 버릴 경우, 이 환경 변수를 설정하고 가끔씩 테스트를 실행하면 발생한 경고를 찾아 제거하는 데 도움이 될 수 있습니다.
autotest에서 디버그 메시지 출력 피하기
성공적으로 통과한 자동 테스트는 처리되지 않은 경고나 디버그 메시지를 생성해서는 안 됩니다. 이렇게 해야 CI 게이트가 새로운 경고나 디버그 메시지를 테스트 실패로 처리할 수 있습니다. 코드를 빌드할 때 컴파일러에서 발생하는 경고와 마찬가지로, 경고가 드물게 발생하면 문제점을 파악하는 유용한 단서가 되지만, 일상적으로 발생한다면 변경 사항을 테스트하는 개발자에게 중요한 문제를 가려버릴 수 있습니다.
개발 중에 디버그 메시지를 추가하는 것은 괜찮지만, 테스트를 체크인하기 전에 이러한 메시지를 비활성화하거나 제거해야 합니다.
잘 구조화된 진단 코드 작성
테스트가 실패할 때 유용한 진단 출력은 주석 처리되거나, 전처리기 지시어로 비활성화되거나, 디버그 빌드에서만 활성화되는 것이 아니라, 일반 테스트 출력의 일부로 포함되어야 합니다. 지속적 통합(CI) 과정에서 테스트가 실패할 경우, 모든 관련 진단 출력이 CI 로그에 기록되어 있다면 진단 코드를 활성화하고 다시 테스트하는 것에 비해 많은 시간을 절약할 수 있습니다. 특히, 실패가 데스크톱에 설치되어 있지 않은 플랫폼에서 발생한 경우라면 더욱 그렇습니다.
테스트 내의 진단 메시지는 stdio.h 또는 iostream.h 출력 메커니즘 대신 qDebug() 및 qWarning() 와 같은 Qt의 출력 메커니즘을 사용해야 합니다. 후자의 경우 Qt의 메시지 처리를 우회하여 -silent 명령줄 옵션으로 진단 메시지를 억제하지 못하게 합니다. 이로 인해 중요한 실패 메시지가 방대한 양의 디버깅 출력 속에 묻혀 버릴 수 있습니다.
테스트 가능한 코드 작성
다음 섹션에서는 테스트하기 쉬운 코드를 작성하기 위한 지침을 제공합니다:
의존성 분리
단위 테스트의 핵심은 모든 클래스를 독립적으로 사용하는 것입니다. 많은 클래스가 다른 클래스를 인스턴스화하기 때문에, 한 클래스만을 따로 인스턴스화하는 것은 불가능합니다. 따라서 객체 생성과 객체 사용을 분리하는 ‘의존성 주입’이라는 기법을 사용해야 합니다. 팩토리는 객체 트리를 구축하는 역할을 담당합니다. 다른 객체들은 추상 인터페이스를 통해 이러한 객체들을 조작합니다.
이 기법은 데이터 중심 애플리케이션에 효과적입니다. GUI 애플리케이션의 경우 객체가 빈번하게 생성 및 소멸되므로 이 접근 방식을 적용하기 어려울 수 있습니다. 추상 인터페이스에 의존하는 클래스의 올바른 동작을 검증하기 위해 모킹을 사용할 수 있습니다. 예를 들어, Googletest 모킹(gMock) 프레임워크를 참고하십시오.
모든 클래스를 라이브러리로 컴파일하기
중소 규모 프로젝트에서는 일반적으로 빌드 스크립트가 모든 소스 파일을 나열한 후 실행 파일을 한 번에 컴파일합니다. 이는 테스트용 빌드 스크립트에서도 필요한 소스 파일을 다시 나열해야 함을 의미합니다.
정적 라이브러리를 빌드하는 스크립트에서 소스 파일과 헤더 파일을 한 번만 나열하는 것이 더 간편합니다. 그러면 ` main() ` 함수는 정적 라이브러리와 링크되어 실행 파일을 빌드하게 되며, 테스트는 정적 라이브러리와 링크됩니다.
여러 프로그램을 빌드하는 데 동일한 소스 파일이 사용되는 프로젝트의 경우, 공유 클래스를 동적 링크(또는 공유 객체) 라이브러리로 빌드하여 테스트 프로그램을 포함한 각 프로그램이 실행 시점에 이를 로드할 수 있도록 하는 것이 더 적절할 수 있습니다. 다시 말해, 컴파일된 코드를 라이브러리에 포함시키면 다양한 프로그램을 만들기 위해 어떤 구성 요소를 결합해야 하는지에 대한 설명을 중복해서 작성하는 것을 피하는 데 도움이 됩니다.
테스트 시스템 구축
다음 섹션에서는 테스트 시스템 설정으로 인해 발생하는 일반적인 문제들에 대해 다룹니다:
이러한 문제들은 일반적으로 가상화 기술을 적절히 활용하면 해결할 수 있습니다.
스크린 세이버
스크린 세이버는 GUI 클래스에 대한 일부 테스트를 방해하여 신뢰할 수 없는 테스트 결과를 초래할 수 있습니다. 테스트 결과가 일관되고 신뢰할 수 있도록 스크린 세이버를 비활성화해야 합니다.
시스템 대화 상자
운영 체제나 다른 실행 중인 애플리케이션에 의해 예기치 않게 표시되는 대화 상자는 자동 테스트에 관련된 위젯에서 입력 포커스를 빼앗아 재현 불가능한 오류를 유발할 수 있습니다.
전형적인 문제의 예로는 macOS의 온라인 업데이트 알림 대화 상자, 바이러스 스캐너의 오경보, 바이러스 시그니처 업데이트와 같은 예약된 작업, 워크스테이션으로 푸시되는 소프트웨어 업데이트, 스택 최상단에 창을 띄우는 채팅 프로그램 등이 있습니다.
디스플레이 사용
일부 테스트는 테스트 머신의 디스플레이, 마우스 및 키보드를 사용하므로, 해당 머신이 동시에 다른 용도로 사용되거나 여러 테스트가 병렬로 실행될 경우 테스트가 실패할 수 있습니다.
CI 시스템은 이러한 문제를 방지하기 위해 전용 테스트 머신을 사용하지만, 전용 테스트 머신이 없는 경우 보조 디스플레이에서 테스트를 실행하여 이 문제를 해결할 수 있습니다.
유닉스에서는 Xephyr와 같은 중첩되거나 가상 X 서버에서 테스트를 실행할 수도 있습니다. 예를 들어, Xephyr에서 전체 테스트 세트를 실행하려면 다음 명령을 실행하십시오:
Xephyr :1 -ac -screen 1920x1200 >/dev/null 2>&1 &
sleep 5
DISPLAY=:1 icewm >/dev/null 2>&1 &
cd tests/auto
make
DISPLAY=:1 make -k -j1 checkNVIDIA 바이너리 드라이버를 사용하는 사용자는 Xephyr가 GLX 확장을 제공하지 못할 수도 있다는 점에 유의해야 합니다. Mesa libGL을 강제로 지정하면 도움이 될 수 있습니다:
export LD_PRELOAD=/usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1그러나 Xephyr과 실제 X 서버에서 서로 다른 libGL 버전을 사용하여 테스트를 실행할 경우, QML 디스크 캐시로 인해 테스트가 중단될 수 있습니다. 이를 방지하려면 ` QML_DISABLE_DISK_CACHE=1`을 사용하십시오.
또는 오프스크린 플러그인을 사용하십시오:
TESTARGS="-platform offscreen" make check -k -j1윈도우 매니저
유닉스에서는 적어도 두 가지 자동 테스트(tst_examples 및 tst_gestures)가 실행 중일 때 창 관리자가 필요합니다. 따라서 중첩된 X 서버에서 이러한 테스트를 실행하는 경우, 해당 X 서버에서도 창 관리자를 실행해야 합니다.
사용 중인 윈도우 매니저는 모든 창을 디스플레이에 자동으로 배치하도록 설정되어 있어야 합니다. Tab Window Manager(twm)와 같은 일부 윈도우 매니저는 새 창을 수동으로 배치하는 모드를 제공하는데, 이 경우 사용자 개입 없이는 테스트 스위트가 실행되지 않습니다.
참고: Tab Window Manager는 Qt 자동 테스트 전체를 실행하는 데 적합하지 않습니다. ‘ tst_gestures ’ 자동 테스트가 실행되면 해당 위젯 매니저가 설정을 잊어버리고 수동 창 배치 모드로 되돌아가기 때문입니다.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.