pytest 기초
pytest는 파이썬 표준 테스팅 프레임워크로, 간결한 문법, 강력한 플러그인 생태계, 상세한 실패 보고서로 가장 널리 쓰입니다.
설치와 첫 테스트
# pip install pytest
# test_hello.py
def add(a: int, b: int) -> int:
return a + b
def test_add_positive():
assert add(1, 2) == 3
def test_add_negative():
assert add(-1, -2) == -3
def test_add_zero():
assert add(0, 0) == 0
# 실행: pytest test_hello.py
# 실행: pytest -v (상세 출력)
# 실행: pytest -v -s (print 출력 포함)
테스트 발견 규칙
프로젝트/
├── src/
│ └── calculator.py
└── tests/
├── conftest.py # 공유 fixture
├── test_calculator.py # test_ 접두사 파일
└── unit/
└── test_utils.py
# pytest 자동 발견 규칙:
# 1. test_*.py 또는 *_test.py 파일
# 2. Test 접두사 클래스 (TestCalculator)
# 3. test_ 접두사 함수/메서드
# test_calculator.py
class TestCalculator:
def test_add(self):
assert 1 + 1 == 2
def test_subtract(self):
assert 5 - 3 == 2
# __init__ 없이 사용 (권장)
assert 재작성 — 상세 실패 메시지
# pytest는 assert를 재작성해 실패 시 상세 정보를 제공
def test_list_comparison():
result = [1, 2, 3, 4]
expected = [1, 2, 3, 5]
assert result == expected
# 실패 시:
# AssertionError: assert [1, 2, 3, 4] == [1, 2, 3, 5]
# At index 3 diff: 4 != 5
def test_string_comparison():
result = "Hello World"
assert "Python" in result
# 실패 시:
# AssertionError: assert 'Python' in 'Hello World'
def test_dict_comparison():
result = {"name": "Alice", "age": 30}
expected = {"name": "Alice", "age": 31}
assert result == expected
# 실패 시: 딕셔너리 diff 표시
# 커스텀 메시지
def test_with_message():
value = 42
assert value > 100, f"값 {value}는 100보다 커야 합니다"
예외 테스트
import pytest
def divide(a: float, b: float) -> float:
if b == 0:
raise ZeroDivisionError("0으로 나눌 수 없습니다")
return a / b
def parse_age(value: str) -> int:
age = int(value)
if age < 0 or age > 150:
raise ValueError(f"유효하지 않은 나이: {age}")
return age
# 예외 발생 확인
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(1, 0)
# 예외 메시지 확인
def test_divide_by_zero_message():
with pytest.raises(ZeroDivisionError, match="0으로 나눌 수 없습니다"):
divide(1, 0)
# 예외 객체 검사
def test_invalid_age():
with pytest.raises(ValueError) as exc_info:
parse_age("200")
assert "200" in str(exc_info.value)
# 예외가 발생하지 않는 경우 테스트
def test_valid_divide():
result = divide(10, 2)
assert result == pytest.approx(5.0)
# 부동소수점 비교
def test_float_comparison():
assert 0.1 + 0.2 == pytest.approx(0.3)
assert 0.1 + 0.2 == pytest.approx(0.3, rel=1e-9)
assert 1.0 == pytest.approx(1.0 + 1e-8, abs=1e-7)
테스트 구성 패턴
import pytest
# AAA 패턴: Arrange, Act, Assert
def test_user_full_name():
# Arrange (준비)
first_name = "Alice"
last_name = "Smith"
# Act (실행)
full_name = f"{first_name} {last_name}"
# Assert (검증)
assert full_name == "Alice Smith"
# GWT 패턴: Given, When, Then (BDD 스타일)
def test_shopping_cart():
# Given
cart = []
item = {"name": "Python 책", "price": 35000}
# When
cart.append(item)
# Then
assert len(cart) == 1
assert cart[0]["name"] == "Python 책"
# 클래스 기반 테스트 (관련 테스트 그룹화)
class TestBankAccount:
def test_initial_balance(self):
balance = 0
assert balance == 0
def test_deposit(self):
balance = 0
balance += 1000
assert balance == 1000
def test_withdraw(self):
balance = 5000
balance -= 2000
assert balance == 3000
pytest 옵션과 실행
# 기본 실행
pytest # 현재 디렉토리 전체
pytest tests/ # tests 폴더만
pytest tests/test_calc.py # 특정 파일
pytest tests/test_calc.py::test_add # 특정 테스트
# 출력 옵션
pytest -v # 상세 (verbose)
pytest -s # print 출력 허용
pytest -v --tb=short # 짧은 트레이스백
pytest --tb=no # 트레이스백 없음
# 필터링
pytest -k "add or subtract" # 이름에 add 또는 subtract 포함
pytest -k "not slow" # slow 제외
pytest -m integration # 마킹된 테스트만
# 실패 제어
pytest -x # 첫 실패 시 즉시 중단
pytest --maxfail=3 # 3번 실패 시 중단
pytest --lf # 마지막으로 실패한 테스트만
pytest --ff # 실패 테스트 먼저 실행
# 병렬 실행 (pip install pytest-xdist)
pytest -n auto # CPU 코어 수만큼 병렬
pytest -n 4 # 4개 워커
pytest.ini / pyproject.toml 설정
# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-v --tb=short"
markers = [
"slow: 느린 테스트",
"integration: 통합 테스트",
"unit: 단위 테스트",
]
# pytest.ini (별도 파일)
[pytest]
testpaths = tests
addopts = -v --tb=short
markers =
slow: marks tests as slow
integration: marks integration tests
정리
| 기능 | 사용법 |
|---|---|
| 기본 assert | assert result == expected |
| 예외 테스트 | pytest.raises(ExcType) |
| 부동소수점 | pytest.approx(value) |
| 출력 보기 | pytest -s |
| 특정 테스트 | pytest -k "test_name" |
| 실패 즉시 중단 | pytest -x |
pytest는 표준 assert를 그대로 사용하면서도 실패 시 상세한 diff를 제공하는 것이 가장 큰 강점입니다.