🚀
고급 (Advanced)
pytest · parametrize · fixture · mock
6주차 — 테스트 (unittest, pytest)
pytest 기본 사용법, parametrize 로 다양한 케이스, fixture 로 공통 setup, 그리고 mock 으로 외부 의존성을 격리하는 방법까지 다룹니다.
pytestfixtureparametrizemock
소요 시간
⏱ 2시간
난이도
📊 고급
선수 조건
🎯 고급 5주차
결과물
pytest 로 자동화된 테스트 작성
이 강의에서 배우는 것
- 1pytest 기본 사용법을 익힌다
- 2parametrize 로 다양한 케이스를 테스트한다
- 3fixture 로 공통 setup 을 제공한다
- 4mock 으로 외부 의존성을 격리한다
1. 왜 테스트?
- 회귀 방지 — 코드 변경 시 동작 보장
- 명세 역할 — 의도 문서화
- 리팩토링 안전망
2. pytest 기본
bash
pip install pytest테스트 파일은 test_*.py 또는 *_test.py 입니다.
python
# test_calc.py
def add(a, b): return a + b
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0bash
pytest # 전체 실행
pytest test_calc.py # 한 파일
pytest -v # verbose
pytest -k "add" # 이름 매칭3. parametrize
같은 테스트를 여러 입력으로.
python
import pytest
@pytest.mark.parametrize("a,b,expected", [
(1, 2, 3),
(-1, 1, 0),
(0, 0, 0),
(100, 200, 300),
])
def test_add(a, b, expected):
assert add(a, b) == expected4. fixture
python
@pytest.fixture
def sample_users():
return [{"name": "Alice"}, {"name": "Bob"}]
def test_first(sample_users):
assert sample_users[0]["name"] == "Alice"
def test_count(sample_users):
assert len(sample_users) == 2스코프(session, module, function):
python
@pytest.fixture(scope="module")
def expensive():
print("한 번만 setup")
return ...5. 예외 검증
python
def divide(a, b):
if b == 0:
raise ValueError("0 division")
return a / b
def test_divide_by_zero():
with pytest.raises(ValueError, match="0 division"):
divide(10, 0)6. mock
외부 의존성(API, DB)을 가짜로.
python
from unittest.mock import patch
def get_weather(city):
import requests
return requests.get(f"https://api/{city}").json()
def test_get_weather():
with patch("requests.get") as mock_get:
mock_get.return_value.json.return_value = {"temp": 20}
assert get_weather("seoul") == {"temp": 20}7. 커버리지
bash
pip install pytest-cov
pytest --cov=mypackage --cov-report=term-missing자주 하는 실수
- 테스트 함수가 test_ 로 시작 안 함 — pytest가 못 찾음
- 테스트끼리 의존 — 실행 순서가 바뀌면 깨짐. 독립적으로
- 전역 상태 변경 — fixture로 setup/teardown
- 외부 API 진짜로 호출 — 느리고 불안정. mock 사용
FAQ
Q1. unittest vs pytest? — 표준은 unittest. 신규는 pytest 압도적 권장.
Q2. TDD 를 꼭? — 아니지만 권장. 핵심 기능은 테스트.
Q3. UI 테스트는? — Selenium, Playwright 등 별도.
💻 예제 (examples)
실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.
01_basic_test.py— 기본 assert
CODE
def add(a, b):
return a + b
def test_add_simple():
assert add(2, 3) == 5
def test_add_negative():
assert add(-1, 1) == 0
# 실행: pytest 01_basic_test.py -v▶ 실행 결과
01_basic_test.py::test_add_simple PASSED
01_basic_test.py::test_add_negative PASSED
2 passed in 0.01s02_parametrize.py— parametrize
CODE
import pytest
def add(a, b):
return a + b
@pytest.mark.parametrize("a,b,expected", [
(1, 2, 3),
(-1, 1, 0),
(0, 0, 0),
(100, 200, 300),
])
def test_add(a, b, expected):
assert add(a, b) == expected
# 실행: pytest 02_parametrize.py -v▶ 실행 결과
test_add[1-2-3] PASSED
test_add[-1-1-0] PASSED
test_add[0-0-0] PASSED
test_add[100-200-300] PASSED
4 passed in 0.02s03_fixture.py— fixture
CODE
import pytest
@pytest.fixture
def sample_users():
return [{"name": "Alice"}, {"name": "Bob"}]
def test_first(sample_users):
assert sample_users[0]["name"] == "Alice"
def test_count(sample_users):
assert len(sample_users) == 2
# 실행: pytest 03_fixture.py -v▶ 실행 결과
test_first PASSED
test_count PASSED
2 passed in 0.01s04_mock.py— unittest.mock
CODE
from unittest.mock import patch
def get_temp(city):
import requests
r = requests.get(f"https://api/{city}")
return r.json()["temp"]
def test_get_temp():
with patch("requests.get") as mock_get:
mock_get.return_value.json.return_value = {"temp": 20}
assert get_temp("seoul") == 20
# 실행: pytest 04_mock.py -v▶ 실행 결과
test_get_temp PASSED
1 passed in 0.05s📝 과제 (exercises)
직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.
과제 1
단위 테스트 5개
목표: 간단한 함수에 대해 테스트 5개를 작성한다.
요구사항
- is_even(n) -> bool 에 대해 짝수/홀수/0 케이스
- sum_list(xs) 에 대해 빈/일반 케이스
💡 힌트
pytest 명령어로 실행
입출력 예시
5 passed채점
- · test_ 함수
- · 5개 PASS
▶정답 코드 펼치기 / 접기
SOLUTION
# test_basics.py
def is_even(n):
return n % 2 == 0
def sum_list(xs):
return sum(xs)
def test_is_even_true():
assert is_even(4) is True
def test_is_even_false():
assert is_even(7) is False
def test_is_even_zero():
assert is_even(0) is True
def test_sum_empty():
assert sum_list([]) == 0
def test_sum_basic():
assert sum_list([1, 2, 3]) == 6
# pytest test_basics.py -v▶ 실행 결과
5 passed in 0.02s과제 2
parametrize 테이블
목표: parametrize 로 6개 케이스 테스트.
요구사항
- max_of(a, b) 에 대해 6개 (a, b, expected) 케이스
💡 힌트
@pytest.mark.parametrize
입출력 예시
6 passed채점
- · 6개 케이스
- · 모두 통과
▶정답 코드 펼치기 / 접기
SOLUTION
import pytest
def max_of(a, b):
return a if a > b else b
@pytest.mark.parametrize("a,b,expected", [
(1, 2, 2),
(5, 3, 5),
(-1, -2, -1),
(0, 0, 0),
(100, 99, 100),
(-5, 0, 0),
])
def test_max_of(a, b, expected):
assert max_of(a, b) == expected
# pytest test_max.py -v▶ 실행 결과
6 passed in 0.02s과제 3
mock 으로 API 테스트
목표: 외부 호출을 mock 으로 대체한다.
요구사항
- fetch_user(uid) 가 requests.get 사용
- patch 로 결과 가짜
💡 힌트
mock_get.return_value.json.return_value
입출력 예시
1 passed채점
- · patch 사용
- · 테스트 PASS
▶정답 코드 펼치기 / 접기
SOLUTION
from unittest.mock import patch
def fetch_user(uid):
import requests
return requests.get(f"https://api/users/{uid}").json()
def test_fetch_user():
with patch("requests.get") as mock_get:
mock_get.return_value.json.return_value = {"id": 1, "name": "Alice"}
result = fetch_user(1)
assert result == {"id": 1, "name": "Alice"}
# pytest test_user.py -v▶ 실행 결과
1 passed in 0.05s