테스트 커버리지 높이기¶
Python 개발에서는 언어와 stdlib(표준 라이브러리)의 모든 의미상 변경과 추가에 적절한 단위 테스트를 함께 작성하는 관행을 따릅니다. 안타깝게도 Python은 이 관행이 시행되기 훨씬 전부터 존재해 왔습니다. 이로 인해 표준 라이브러리의 상당 부분이 테스트되지 않은 채로 남아 있으며, 이는 바람직한 상황이 아닙니다.
Python 코드에 익숙해지면서 도움도 줄 수 있는 좋고 쉬운 방법은 Python 표준 라이브러리의 테스트 커버리지를 높이는 데 힘을 보태는 것입니다. 이상적으로는 커버리지 100%를 달성하고 싶지만, 조금이라도 높아진다면 모두 좋은 일입니다. 하지만 커버리지 100%를 달성하는 것이 항상 가능하지는 않다는 점을 알아 두십시오. 여러분의 환경에서는 아예 실행되지 않는 플랫폼별 코드나 출력의 오류 등이 있을 수 있습니다. 무엇을 커버해야 하고 무엇을 커버하지 않아도 되는지는 스스로 판단하면 되지만, 보수적으로 접근하여 커버해야 한다고 가정하는 것이 일반적으로 따르기 좋은 원칙입니다.
테스트 커버리지를 높일 모듈은 몇 가지 방법으로 고를 수 있습니다. 커버리지를 켠 상태로 전체 테스트 스위트를 직접 실행하여 어떤 모듈에 도움이 필요한지 확인하면 됩니다. 이 방법은 커버리지를 측정하면서 전체 테스트 스위트를 실행하므로 완료하는 데 시간이 다소 걸린다는 단점이 있지만, 어떤 모듈에 가장 많은 작업이 필요한지 정확한 최신 정보를 얻을 수 있습니다.
다른 방법은 아래 예제를 따라 여러분이 좋아하는 모듈의 커버리지가 어느 정도인지 확인하는 것입니다. 하지만 이 방법은 “어둠 속에서 찌르기”와 같아서, 커버리지 개선이 필요한 모듈을 찾는 데 시간이 걸릴 수 있습니다.
다만 작업하기로 결정한 모듈이 무엇이든, 반드시 그 모듈에 대해서만 커버리지를 실행하십시오. 이렇게 하면 해당 모듈을 우연히 사용하는 다른 코드에 의한 암묵적인 테스트가 아니라, 모듈 자체의 테스트 모음에 의한 명시적인 커버리지가 얼마나 좋은지를 확실히 알 수 있습니다.
흔히 빠지는 함정¶
커버리지 데이터 기록이 시작되기 전에 이미 임포트된 모듈에 대한 커버리지 보고서는 잘못된 결과를 보여 준다는 점을 알아 두십시오. 일반적으로 임포트할 때 당연히 실행되었을 전역 문장은 실행되지 않은 것으로, 지역 문장은 커버된 것으로 커버리지 보고서에 나타난다면 해당 모듈이 이 경우에 해당한다는 것을 알 수 있습니다. 이런 경우에는 전역 문장 커버리지를 무시하고 지역 문장 커버리지에만 집중하면 됩니다.
커버리지를 높이기 위해 새 테스트를 작성할 때는 모듈에 이미 있는 테스트의 스타일(예: 화이트박스, 블랙박스 등)에 유의하십시오. 일부 모듈은 주로 한 명의 코어 개발자가 유지 관리하므로, 그 개발자가 사용할 테스트 종류에 대해 특정한 선호(예: 화이트박스)가 있어 다른 종류의 테스트(예: 블랙박스)는 사용하지 않기를 바랄 수 있습니다. 확신이 서지 않을 때는 코드를 제대로 실행해 보기 위해 화이트박스 테스트를 고수하십시오.
커버리지 측정하기¶
Python 자체의 표준 라이브러리에 대해 커버리지를 실행할 때 알아 두어야 할 특이점은 일부 모듈이 인터프리터 시작 과정에서 임포트된다는 것입니다. Python 자체에 필요한 이러한 모듈은 커버리지 도구에서 실행된 것으로 간주되지 않으므로 커버리지가 매우 낮은 것처럼 보입니다(예: stat 모듈). 이런 경우 해당 모듈은 전역 문장이 전혀 커버되지 않은 것처럼 보이지만 지역 문장은 제대로 커버됩니다(예: 함수 정의는 추적되지 않지만 함수 본문은 추적됩니다). 이런 상황에서 모듈의 커버리지를 계산하려면 어떤 지역 문장이 실행되지 않았는지 직접 살펴보기만 하면 됩니다.
coverage.py 사용하기¶
가장 널리 쓰이는 서드파티 커버리지 도구 중 하나는 coverage.py이며, 매우 보기 좋은 HTML 출력과 함께 분기 커버리지 같은 고급 기능을 제공합니다. 표준 라이브러리가 제공하는 도구만 사용하고 싶다면 test.regrtest를 사용할 수 있습니다.
coverage 설치하기¶
기본적으로 pip는 방금 빌드한 개발 중인 버전의 Python에 패키지를 설치하지 않으며, 이렇게 빌드한 Python은 기본 Python 버전에 설치된 패키지를 인식하지 못합니다. 한 가지 방법은 가상 환경을 사용하여 coverage를 설치하는 것입니다.
다음을 실행하십시오:
./python -m venv ../cpython-venv
source ../cpython-venv/bin/activate
pip install coverage
대부분의 macOS 시스템에서는 다음을 실행하십시오:
./python.exe -m venv ../cpython-venv
source ../cpython-venv/bin/activate
pip install coverage
다음을 실행하십시오:
python.bat -m venv ..\\cpython-venv
..\\cpython-venv\\Scripts\\activate.bat
pip install coverage
이제 venv가 활성화되어 있는 한, 이 지침의 나머지 부분에서는 ./ 없이 python을 사용할 수 있습니다. venv에 관한 자세한 내용은 가상 환경 문서를 참조하십시오.
어떤 이유로든 이 방법이 작동하지 않는다면, 개발 중인 버전의 coverage.py를 사용하여 필요한 대로 갱신되었는지 확인해 보십시오. 이렇게 하려면 coverage.py의 개발 버전을 클론/체크아웃해야 합니다:
git clone https://github.com/nedbat/coveragepy
설치 위치의 전체 경로를 사용해야 합니다.
또 다른 방법은 이미 설치된 coverage.py 사본이 있다면 그것을 사용하는 것입니다. 이 경우에도 마찬가지로 해당 설치 위치의 전체 경로를 사용해야 합니다.
기본 사용법¶
다음 명령은 여러분의 coverage 사본이 작동하는지 알려 줍니다(COVERAGEDIR를 클론이 있는 디렉터리로 바꾸십시오. 예: ../coveragepy):
./python COVERAGEDIR
Coverage.py는 모든 것이 제대로 작동하는지 확인하는 간단한 도움말 텍스트를 출력합니다. 설치된 사본을 사용하고 있다면 대신 다음과 같이 할 수 있습니다(이 사본은 venv 등을 통해 빌드한 Python으로 설치되어 있어야 한다는 점에 유의하십시오):
./python -m coverage
이후의 coverage.py 사용 예제는 클론한 사본을 사용한다고 가정하지만, 위의 방법으로 바꾸어도 모든 지침은 여전히 유효합니다.
coverage.py로 테스트 스위트를 실행하려면 다음과 같이 하십시오:
./python COVERAGEDIR run --pylib Lib/test/regrtest.py
단일 테스트만 실행하려면 --source 플래그에 테스트 대상 모듈/패키지를 지정하고(관심 있는 모듈/패키지로만 커버리지 보고 범위를 좁히기 위함입니다) 실행하려는 테스트의 이름을 명령 끝에 덧붙이십시오:
./python COVERAGEDIR run --pylib --source=abc Lib/test/regrtest.py test_abc
커버리지 실행 결과를 확인하려면 다음 명령으로 텍스트 기반 보고서를 볼 수 있습니다:
./python COVERAGEDIR report
--show-missing 플래그를 사용하면 실행되지 않은 줄의 목록을 얻을 수 있습니다:
./python COVERAGEDIR report --show-missing
하지만 coverage.py의 강점 중 하나는 테스트되지 않은 코드 줄을 시각적으로 확인할 수 있는 HTML 기반 보고서입니다:
./python COVERAGEDIR html -i --include=`pwd`/Lib/* --omit="Lib/test/*,Lib/*/tests/*"
이 명령은 htmlcov 디렉터리에 HTML 보고서를 생성하며, 발생할 수 있는 모든 오류를 무시하고 테스트 커버리지가 중요하지 않은 모듈(예: 테스트, 임시 파일 등)도 무시합니다. 그런 다음 웹 브라우저에서 htmlcov/index.html 파일을 열어 커버리지 결과와 함께 어떤 코드 줄이 실행되었고 어떤 코드 줄이 실행되지 않았는지 눈으로 보여 주는 페이지를 볼 수 있습니다.
브랜치 커버리지¶
정말 과감한 분이라면 coverage.py의 또 다른 강력한 기능인 브랜치 커버리지를 사용할 수 있습니다. 코드를 지나는 모든 가능한 브랜치 경로를 테스트하는 것은 추구할 만한 훌륭한 목표이지만, (현재로서는) 표준 라이브러리 전체에 대해 100% 줄 커버리지를 달성하는 것에 비하면 부차적인 목표입니다.
브랜치 커버리지를 개선해 보기로 했다면, 커버리지 실행에 --branch 플래그를 추가하기만 하면 됩니다:
./python COVERAGEDIR run --pylib --branch <arguments to run test(s)>
이렇게 하면 보고서에 어떤 줄이 커버되지 않았는지뿐만 아니라 어떤 브랜치 경로가 실행되지 않았는지도 표시됩니다.
test.regrtest 사용하기¶
커버리지 데이터를 생성할 때 표준 라이브러리에만 의존하고 싶다면, test에 적절한 플래그를 (원하는 다른 플래그와 함께) 전달하면 됩니다:
./python -m test --coverage -D `pwd`/coverage_data <test arguments>
-D에 전달하는 인자에 유의하십시오. 커버리지 데이터를 저장할 위치를 절대 경로로 지정하지 않으면 예상하지 못한 곳에 저장됩니다.
참고
전체 테스트 스위트에 대해 커버리지를 실행한다면, 반드시 -x test_importlib test_runpy test_trace를 추가하여 해당 테스트들을 제외하십시오. 이 테스트들은 커버리지 측정 중에 예외를 일으킵니다. python/cpython#54750 및 python/cpython#55200을 참고하십시오.
테스트가 끝나면 지정한 디렉터리에 실행된 각 모듈의 파일이 생성되어 있으며, 각 파일에는 어떤 줄이 몇 번 실행되었는지가 기록되어 있습니다.
이슈 등록하기¶
커버리지를 높였다면 issue tracker에 이슈를 만들고 풀 리퀘스트를 제출해야 합니다.
gcov와 lcov로 C 코드의 커버리지 측정하기¶
Python C 코드의 함수, 줄, 브랜치 커버리지를 측정하는 것도 가능합니다. 현재는 gcov를 사용하는 GCC만 지원됩니다. gcov로 계측된 Python 빌드를 만들려면 다음을 실행하십시오:
make coverage
.\make coverage
그런 다음 코드를 실행하고 gcov 명령으로 커버리지 데이터를 수집하십시오. HTML 보고서를 만들려면 lcov를 설치하면 됩니다. 다음 명령은:
make coverage-lcov
.\make coverage-lcov
커버리지 데이터를 모으고, 서드파티 라이브러리와 시스템 라이브러리를 제거한 다음, 마지막으로 보고서를 생성합니다. 두 단계를 모두 건너뛰고 다음 명령만 실행할 수도 있습니다:
make coverage-report
.\make coverage-report
Python 표준 라이브러리 테스트에 대한 커버리지 보고서를 생성하려는 경우입니다. 최신 컴퓨터에서 약 20~30분이 걸립니다.
참고
여러 개의 테스트 작업을 동시에 실행하면 제대로 동작하지 않을 수 있습니다. C 커버리지 보고는 단일 테스트 프로세스로만 테스트되었습니다.