본문 바로가기
C.W.K.
Stream
Lesson 01 of 05 · published

pytest — 적은 격식으로 또렷하게 시험하기

~22 min · pytest, testing, fixture, parametrize

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

표준 라이브러리의 unittest도 쓸 수 있지만 pytest는 TestCase 상속이나 self.assertEqual 없이 test_ 함수와 평범한 assert로 시작해. 실패값을 풍부하게 보여주고 파일·함수를 자동 발견하며, 특정 테스트는 pytest path/to/file.py::test_name으로 골라 실행할 수 있어.

픽스처는 준비값을 주고 yield 뒤에서 정리해. 함수·모듈·세션 범위로 수명을 정하고, 매개변수화는 같은 검증을 여러 입력 행에 반복해 각 행을 별 테스트로 보여줘. 비동기 함수에는 pytest-asyncio나 pytest-anyio가 필요해.

pytest는 빠른 회귀 안전망이지 운영 환경 증명이 아니야. CWK에서는 소스 단계의 테스트 뒤에도 설치본, 실제 서비스, 브라우저 경로를 확인해야 끝이야.

Code

가장 작은 pytest 파일·python
# test_math.py
def add(a, b):
    return a + b

def test_add_basic():
    assert add(2, 3) == 5

def test_add_negative():
    assert add(-1, 1) == 0

def test_add_floats():
    result = add(0.1, 0.2)
    assert abs(result - 0.3) < 1e-10        # float 비교

# 실행 — pytest test_math.py
# 출력:
# test_math.py::test_add_basic PASSED
# test_math.py::test_add_negative PASSED
# test_math.py::test_add_floats PASSED
yield 픽스처로 준비와 정리 묶기·python
import pytest
import tempfile
from pathlib import Path

@pytest.fixture
def temp_file():
    # Setup
    f = tempfile.NamedTemporaryFile(delete=False, suffix=".txt")
    f.write(b"hello")
    f.close()
    path = Path(f.name)
    yield path                              # 테스트에 제공
    # Teardown — 테스트 후 실행, 실패해도
    path.unlink(missing_ok=True)

def test_read(temp_file):
    content = temp_file.read_text()
    assert content == "hello"

# 이름으로 fixture — pytest 가 파라미터를 fixture 매치
매개변수화로 입력 표 만들기·python
import pytest

def divide(a, b):
    return a / b

@pytest.mark.parametrize("a,b,expected", [
    (10, 2, 5),
    (9, 3, 3),
    (1, 4, 0.25),
    (-6, 2, -3),
])
def test_divide(a, b, expected):
    assert divide(a, b) == expected

# 각 row 가 별 테스트로 실행:
# test_divide[10-2-5] PASSED
# test_divide[9-3-3] PASSED
# test_divide[1-4-0.25] PASSED
# test_divide[-6-2--3] PASSED

# 5 번째 입력 추가가 한 줄
pytest.raises로 예외 확인하기·python
import pytest

def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("can't")
    return a / b

def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError, match="can't"):
        divide(10, 0)

# raises 가 예외 검사에도
def test_with_inspection():
    with pytest.raises(ValueError) as exc_info:
        raise ValueError("specific message")
    assert "specific" in str(exc_info.value)
pytest-asyncio로 비동기 함수 시험하기·python
# pip install pytest-asyncio
#
# pyproject.toml 에:
# [tool.pytest.ini_options]
# asyncio_mode = "auto"        # async 테스트 자동 검출

import pytest
import asyncio

async def fetch(name):
    await asyncio.sleep(0.01)
    return f"got {name}"

@pytest.mark.asyncio                       # 또는 auto 모드가 잡음
async def test_fetch():
    result = await fetch("foo")
    assert result == "got foo"

@pytest.mark.asyncio
async def test_concurrent():
    a, b = await asyncio.gather(fetch("a"), fetch("b"))
    assert a == "got a"
    assert b == "got b"

External links

Exercise

add·subtract·multiply·divide를 가진 계산기 모듈을 만들고 기본 테스트를 써. 앞의 세 연산은 각각 입력 조합 다섯 개 이상을 매개변수화로 검사하고, 0 나눗셈은 pytest.raises로 확인해. 난수 쌍 100개를 준비하는 픽스처로 네 연산도 모두 실행해봐.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.