파이썬 pytest 사용법
자동 테스트 첫 발걸음. 자신감 있게 리팩토링하는 도구.
22편의 자동화 스크립트가 6개월간 잘 돌다가 어느 날 갑자기 잘못된 결과를 냅니다. 누군가 한 줄을 고쳤거든요. 사람 손으로 매번 같은 입력을 넣어 같은 결과인지 확인하는 건 불가능. 자동 테스트가 답입니다 — 코드 한 줄 고치고 pytest 명령 하나 치면 30초 안에 "고친 부분이 다른 곳을 깨뜨리지 않았는지" 확인.
25편을 마치면 ① pytest 기본 ② assert 문 ③ fixture 로 공유 자원 ④ parametrize 로 여러 케이스 ⑤ mock·coverage 도구 — 5가지가 손에 익습니다.
첫 테스트 — assert 한 줄
# calc.py
def add(a: int, b: int) -> int:
return a + b
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("0 으로 나눌 수 없음")
return a / b
# test_calc.py — 같은 폴더에
from calc import add, divide
import pytest
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0
def test_divide():
assert divide(10, 2) == 5
assert divide(7, 2) == 3.5
def test_divide_zero():
with pytest.raises(ValueError):
divide(10, 0)
(.venv) $ pip install pytest
(.venv) $ pytest
test_calc.py ... [100%]
==== 3 passed in 0.02s ====
규칙은 단순. ① 파일명 test_*.py 또는 *_test.py ② 함수명 test_* ③ assert 한 줄로 기대값 확인. pytest 가 자동으로 찾아 실행합니다.
📌 좋은 테스트의 3요소 (AAA 패턴)
· Arrange — 준비 (입력·객체 만들기)
· Act — 실행 (테스트할 함수 호출)
· Assert — 확인 (결과가 기대와 같은가)
이 3단계가 한 함수에 깔끔히 보이면 잘 쓴 테스트.
fixture — 여러 테스트가 공유하는 준비물
import pytest
@pytest.fixture
def sample_users():
"""3명짜리 테스트 사용자 리스트."""
return [
{"name": "준성", "age": 30},
{"name": "수민", "age": 28},
{"name": "지훈", "age": 35},
]
def test_count(sample_users):
assert len(sample_users) == 3
def test_avg_age(sample_users):
ages = [u["age"] for u in sample_users]
assert sum(ages) / len(ages) == 31
# 인자 이름이 fixture 이름과 일치하면 자동 주입
임시 파일·DB 연결·테스트 데이터처럼 여러 테스트가 같은 준비물을 쓸 때. tmp_path·monkeypatch 같은 기본 제공 fixture 도 많습니다.
# tmp_path — 자동 정리되는 임시 폴더
def test_write_file(tmp_path):
f = tmp_path / "data.txt"
f.write_text("hello")
assert f.read_text() == "hello"
# 테스트 끝나면 자동 삭제
parametrize — 한 테스트로 여러 케이스
@pytest.mark.parametrize("a, b, expected", [
(2, 3, 5),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
])
def test_add_many(a, b, expected):
assert add(a, b) == expected
# 한 번 실행에 4개 테스트가 돌아감
여러 입력에 같은 함수를 시험할 때. 케이스 추가가 한 줄로 끝나고, 어느 케이스가 실패했는지도 명확히 보입니다.
mock·coverage — 두 보조 도구
mock — 외부 의존을 가짜로
# 20편 requests 를 쓰는 함수를 테스트
import requests
def get_weather(city: str) -> str:
r = requests.get(f"https://api.weather.com/{city}", timeout=10)
return r.json()["temp"]
# 테스트에서 실제 호출 X
from unittest.mock import patch
def test_get_weather():
with patch("requests.get") as mock_get:
mock_get.return_value.json.return_value = {"temp": "20°C"}
assert get_weather("seoul") == "20°C"
mock_get.assert_called_once_with(
"https://api.weather.com/seoul", timeout=10
)
네트워크·DB·파일 시스템처럼 외부에 의존하는 부분은 가짜로 대체. 테스트가 빨라지고(0.001초), 인터넷 없어도 통과, 결과가 일관됩니다.
coverage — 어디까지 테스트됐나
(.venv) $ pip install pytest-cov
(.venv) $ pytest --cov=calc --cov-report=term-missing
Name Stmts Miss Cover Missing
---------------------------------------
calc.py 10 2 80% 14, 17
---------------------------------------
TOTAL 10 2 80%
14·17 줄은 어떤 테스트도 안 들렀다는 뜻. 100% 가 목표는 아니지만, 핵심 로직에 80%+ 가 안 들면 회귀 위험이 높습니다.
다음 미션: ① 9편의 grade 함수에 5개 이상 parametrize 테스트 ② tmp_path fixture 로 파일 쓰기·읽기 테스트 ③ mock 으로 외부 API 호출 함수 테스트.
다음 편 미리보기
26편 — "Black·Ruff": 코드 스타일 자동 통일과 정적 분석. 한 번 깔면 PR 코멘트 절반이 사라진다.
← 24편 "async·await" · 다음: 26편 "Black·Ruff"