파이썬 교재 · 25편 / 27편

파이썬 pytest 사용법

자동 테스트 첫 발걸음. 자신감 있게 리팩토링하는 도구.

실전읽는 시간 7분2026-05-13
test_add 함수와 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%+ 가 안 들면 회귀 위험이 높습니다.

⚠️ 테스트를 너무 많이 쓰지 마세요. 모든 함수에 테스트는 과합니다. ① 입력에 따라 결과가 갈리는 로직(if·계산·변환) ② 외부에 노출되는 함수(API·CLI) ③ 한 번 버그 났던 곳(회귀 방지) — 이 셋이 우선. 단순 setter·getter·UI 호출은 안 적어도 OK.

다음 미션: ① 9편의 grade 함수에 5개 이상 parametrize 테스트 ② tmp_path fixture 로 파일 쓰기·읽기 테스트 ③ mock 으로 외부 API 호출 함수 테스트.

다음 편 미리보기

26편 — "Black·Ruff": 코드 스타일 자동 통일과 정적 분석. 한 번 깔면 PR 코멘트 절반이 사라진다.

📚 27편 파이썬 교재 시리즈 — 25/27편
← 24편 "async·await" · 다음: 26편 "Black·Ruff"

© 2026 주나이테크(주) @JUNAITECH