Python 트랙에서 테스트하기

Exercism에서 Python 연습 문제를 테스트하는 방법을 배워요.


웹사이트 테스트 실행기로 pytest를 사용해요. Python 트랙의 연습 문제 테스트를 로컬에서 내려받아 실행하려면 개발 컴퓨터에 pytest를 설치해야 해요. 다음 pytest 플러그인도 설치하는 게 좋아요:

또한 코드 린팅 프로그램인 pylint 사용을 권장하는데, 웹사이트의 자동 피드백에 포함되어 있고 매우 유용한 정적 코드 분석 도구가 될 수 있어요. 편의를 위해 pytest용 pytest-pylint 플러그인을 사용하면 명령줄에서 pytest를 통해 pylint를 실행할 수 있어요.

Pylint 설정은 다소 복잡할 수 있는데, 시작하는 데에는 pylint.readthedocs.io의 튜토리얼이 도움이 되고, Real Python의 Code Quality: Tools and Best Practices 개요도 도움이 돼요.

pytest 설치

pytest는 Python에 내장된 유틸리티인 pip로 설치하고 업데이트할 수 있어요.

추가 팁으로, Brett Cannon이 python 패키지 설치에 관한 간단하고 실용적인 가이드와 python -m pip를 사용해야 하는 이유에 대한 훌륭한 설명을 작성했어요. Python의 명령줄 인수에 대한 더 자세한 내용은 Python 문서의 명령줄과 환경을 참고해요.

참고: 시스템에 따라 Python3와 py가 Python의 별칭일 수도, 아닐 수도 있어요. 아래 설치 명령어를 그에 맞게 조정해요. 가상 환경에 pytest를 설치하려면 명령을 실행하기 전에 환경이 활성화되어 있는지 확인해요. 그렇지 않으면 pytest가 전역으로 설치돼요.

Windows

PS C:\Users\foobar> py -m pip install pytest pytest-cache pytest-subtests pytest-pylint
Successfully installed pytest-8.3.3 ...

Linux / MacOS

$ python3 -m pip install pytest pytest-cache pytest-subtests pytest-pylint
Successfully installed pytest-8.3.3 ...

설치가 성공했는지 확인하려면:

$ python3 -m pytest --version
pytest 8.3.3

테스트 실행하기

테스트를 실행하려면 터미널에서 cd를 사용해 연습 문제가 저장된 폴더로 이동해요 (아래 <exercise-folder-location>을 자신의 경로로 바꿔요).

$ cd <exercise-folder-location>

Note

<exercise-folder-location> 또는 꺾쇠괄호 안에 있는 대부분의 것은 **자리 표시자 값**을 나타내요. 일반적인 경로나 파일 이름은 괄호 없이 작성해야 해요.

예를 들어 /Users/janedoe/exercism/python/exercises/concept/chaitanas-colossal-coaster (*nix 시스템), C:\Users\janedoe\exercism\python\exercises\practice\hello-world\ (Windows), myFolder, my_file.py 같은 것이에요.


실행하려는 파일은 보통 _test.py로 끝나요. 이 파일에는 연습 문제 풀이에 대한 테스트가 들어 있고, 풀이를 업로드할 때 웹사이트에서 실행되는 것과 같은 테스트예요. 다음으로, 터미널에서 다음 명령을 실행하는데, <exercise_test.py>를 테스트 파일의 위치/이름으로 바꿔요:

$ python3 -m pytest -o markers=task <exercise_test.py>
==================== 7 passed in 0.08s ====================

경고 해결하기

위 명령에서 pytest -o markers=task를 사용하지 않으면, 새로운 문법을 사용하는 테스트를 실행할 때 "unknown markers"에 관한 warnings가 표시될 수 있어요.

실행하는 모든 테스트마다 pytest -o markers=task를 입력하기 번거로우면 pytest.ini 설정 파일을 사용할 수 있는데, 이 파일은 Python 트랙 디렉터리의 최상위에서 내려받을 수 있어요: pytest.ini.

다음 내용으로 자신만의 pytest.ini 파일을 만들어도 돼요:

[pytest]
markers =
    task: A concept exercise task.

이 파일을 Exercism 연습 문제의 루트 또는 작업 디렉터리에 두면 마크가 등록되고 경고가 사라져요. pytest 마크에 대한 더 자세한 내용은 테스트 함수에 마크 지정하기와 사용자 정의 마커 사용하기에 관한 pytest 문서에서 찾을 수 있어요.

pytest 설정을 사용자 정의하는 방법에 대한 더 자세한 내용은 설정 파일 형식에 관한 pytest 문서에서 찾을 수 있어요

테스트 실패

테스트가 실패하면 pytest는 터미널에 실패한 각 테스트의 텍스트와 함께 각 테스트의 예상 return 값과 실제 값을 출력해요. 다음은 실패한 테스트의 일반적인 예예요:

$(my_venv) python3 -m pytest -o markers=task <exercise_test.py>

=================== FAILURES ====================
______________ name_of_failed_test ______________
# Test code inside of <exercise_test.py> that failed.
...
E   TypeOfError: ReturnedValue != ExpectedValue

exercise_test.py:<line_of_failed_test>: TypeOfError
============ short test summary info ============
FAILED exercise_test.py::ExerciseTest::name_of_failed_test
========== 1 failed, 2 passed in 0.13s ==========

추가 인수

pytest가 화면에 반환하는 내용을 정말 구체적으로 지정하고 싶다면, 동작을 설정할 수 있는 유용한 명령줄 인수가 몇 가지 있어요.

모든 세부 정보 반환하기 [-v]

-v (verbose) 플래그를 추가하면 테스트 실패 외에도 환경 정보와 테스트 요약을 함께 반환해요:

$(my_venv)  python3 -m pytest -o markers=task -v exercises/<exercise_name>/<test_file_test.py>

======================================== test session starts ===========================================
platform darwin -- Python 3.9.0, pytest-6.2.5, -- /usr/local/envs/my_env/bin/python3
cachedir: .pytest_cache
metadata: {'Python': '3.9.0', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '6.2.1'}, 'Plugins': {'subtests': '0.5.0', 'pylint': '0.18.0'}
rootdir: /Users/<user>/exercism/python, configfile: pytest.ini
plugins: subtests-0.5.0, pylint-0.18.0

collected 5 items

exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_one FAILED                          [ 20%]
exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_two FAILED
exercises/exercise-name/exercise_file_test.py::ExerciseNameTest::test_three PASSED                        [ 40%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_four FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_five PASSED                 [ 60%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_six FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_seven PASSED                [ 80%]
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_eight FAILED
exercises/concept/exercise-name/exercise_file_test.py::ExerciseNameTest::test_nine PASSED                 [100%]

================================================ FAILURES ======================================================
# Failed tests are then individually printed out below

.......

첫 실패 후 중단하기 [-x]

-x 플래그를 사용하면 테스트를 평소처럼 실행하지만, 첫 테스트 실패 시 실행을 중단해요. 한 번에 하나의 작업이나 테스트 실패를 디버깅하고 싶을 때 도움이 돼요:

$(my_venv) python3 -m pytest -o markers=task -x exercises/<exercise_name>/<test_file_test.py>

=================== FAILURES ====================
_______________ example_test_foo ________________
...
...
============ short test summary info ============
FAILED example_test.py::ExampleTest::example_test_foo
!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!
========== 1 failed, 5 passed in 0.28s ==========

실패한 테스트 먼저 실행하기 [--ff]

pytest-cache 플러그인은 지난번 pytest를 실행했을 때 어떤 테스트가 실패했는지 기억하므로, --ff 플래그를 사용하면 pytest가 이전에 실패한 테스트를 먼저 실행하고 나머지 테스트를 이어서 실행하도록 해요. 특정 작업이나 입력 집합을 중심으로 작은 수정을 많이 하고 있다면 테스트 속도가 빨라질 수 있어요.

$(my_venv) python3 -m pytest -o markers=task --ff <example_file_test.py>
==================== 7 passed in 503s ====================

권장 워크플로

디버깅을 더 쉽고 (어쩌면) 더 빠르게 하려면 다음 명령을 사용하는 것을 권장해요:

먼저 테스트하려는 연습 문제의 디렉터리로 작업 디렉터리를 바꿔요:

$(my_venv) cd path/to/exercise

그런 다음, 앞에서 설명한 -x와 --ff 인수와 함께 테스트를 실행해요:

$(my_venv) python3 -m pytest -o markers=task -x --ff <example_file_test.py>

이렇게 하면 풀이가 테스트돼요. pytest가 실패한 테스트를 만나면 프로그램이 멈추고 어떤 테스트가 실패했는지 알려줘요. 수정한 뒤 테스트를 다시 실행하면, pytest는 이전에 실패했던 테스트를 먼저 실행하고 나머지 테스트를 이어서 실행해요.

pytest와 함께 Python 디버거 PDB 사용하기

"프로처럼 디버깅"하고 싶다면 pytest 명령 뒤에 --pdb 인수를 사용해 내장 Python 디버거인 PDB로 들어갈 수 있어요.

$(my_venv) python3 -m pytest -o markers=task -x --ff --pdb <example_file_test.py>
=============== 4 passed in 0.15s ===============

테스트가 실패하면 PDB로 들어가서 현재 스코프를 보며 코드를 한 단계씩 실행하고, 변수의 값과 여러 함수의 시그니처를 확인할 수 있어요. PDB 모듈에 대한 더 자세한 내용은 PDB에 관한 Python 문서에서 찾을 수 있어요. 또한 PDB에 관한 pytest 문서와 Real Python의 이 가이드가 매우 도움이 돼요.

IDE 확장하기

테스트와 코드 개선에 도움이 되는 도구로 IDE를 확장하고 싶다면 tools 페이지를 확인해요. 그곳에서 여러 IDE와 에디터, 린팅과 디버깅에 유용한 몇 가지 확장 기능을 살펴봐요.

추가 정보

PATH에 python 추가하기

참고: Windows에서 PSF Installer로 Python을 설치했다면, 명령은 python3가 아니라 py예요.

모듈을 실행할 때마다 python3 -m을 입력하는 게 조금 번거로울 수 있어요. 이를 피하려면 Python 설치 폴더의 Scripts 폴더를 PATH에 추가하면 돼요. Python을 어디에 설치했는지 모르겠다면, 터미널에서 다음 명령을 실행해요:

$ python3 -c "import os, sys; print(os.path.dirname(sys.executable))"
<python_directory>

반환된 디렉터리가 현재 활성화된 Python 버전이 설치된 곳이며, 이 섹션에서는 이를 <python_directory>라고 불러요.

Windows

Windows Start 버튼을 클릭하고 _Edit the system environment variables_를 검색한 뒤 엔터를 눌러요. 그다음 Environment Variables...를 눌러요:

파란 버튼을 눌러요, ㅋㅋ

그런 다음 _User variables_에서 Path 변수를 찾아 선택하고 Edit...를 클릭해요:

path 변수 선택하기

그런 다음 그림과 같이 새 줄을 추가하는데, <python_directory>를 Python 설치 디렉터리로 바꿔요:

path에 python 추가하기

MacOS/Linux

아래 내용은 bash 셸을 사용하는 대부분의 Linux 및 MacOS 배포판에서 작동해요. 명령은 Linux 배포판과 fish 또는 zsh 셸 사용 여부에 따라 달라질 수 있어요. <python_directory>를 python3 -c "import os, sys; print(os.path.dirname(sys.executable))"의 출력으로 바꿔요.

export PATH="$PATH:<python_directory>"