본문으로 건너뛰기
Advertisement

fixture — 테스트 픽스처

fixture는 테스트에 필요한 데이터, 객체, 연결 등을 미리 준비하는 함수입니다. @pytest.fixture 데코레이터로 정의하고, 테스트 함수의 매개변수로 주입받습니다.


기본 fixture

import pytest


# fixture 정의
@pytest.fixture
def sample_list():
return [1, 2, 3, 4, 5]


@pytest.fixture
def user_dict():
return {"name": "Alice", "age": 30, "email": "alice@example.com"}


# 매개변수 이름으로 자동 주입
def test_list_sum(sample_list):
assert sum(sample_list) == 15


def test_list_length(sample_list):
assert len(sample_list) == 5


def test_user_name(user_dict):
assert user_dict["name"] == "Alice"


def test_user_age(user_dict):
assert user_dict["age"] == 30


# 여러 fixture 동시 사용
def test_combined(sample_list, user_dict):
assert len(sample_list) == user_dict["age"] - 25

setup / teardown — 자원 정리

import pytest
import tempfile
import os


# yield 이전: setup, yield 이후: teardown
@pytest.fixture
def temp_file():
# Setup: 임시 파일 생성
fd, path = tempfile.mkstemp(suffix=".txt")
os.close(fd)
print(f"\n[Setup] 임시 파일 생성: {path}")

yield path # 테스트에 path 전달

# Teardown: 파일 삭제
if os.path.exists(path):
os.remove(path)
print(f"\n[Teardown] 임시 파일 삭제: {path}")


def test_write_file(temp_file):
with open(temp_file, "w") as f:
f.write("Hello pytest")

with open(temp_file) as f:
content = f.read()

assert content == "Hello pytest"


def test_file_exists(temp_file):
assert os.path.exists(temp_file)


# 데이터베이스 연결 시뮬레이션
@pytest.fixture
def db_connection():
print("\n[Setup] DB 연결")
conn = {"connected": True, "data": {}}

yield conn

print("\n[Teardown] DB 연결 해제")
conn["connected"] = False


def test_db_insert(db_connection):
db_connection["data"]["user1"] = {"name": "Bob"}
assert "user1" in db_connection["data"]


def test_db_empty_at_start(db_connection):
# 각 테스트마다 새로운 fixture 인스턴스
assert len(db_connection["data"]) == 0

fixture 스코프

import pytest


# scope: function(기본), class, module, session
@pytest.fixture(scope="function")
def func_fixture():
"""각 테스트마다 새로 생성"""
print("\n[function] setup")
yield {"count": 0}
print("\n[function] teardown")


@pytest.fixture(scope="module")
def module_fixture():
"""모듈(파일) 당 한 번만 생성"""
print("\n[module] setup")
yield {"shared": True, "calls": 0}
print("\n[module] teardown")


@pytest.fixture(scope="session")
def session_fixture():
"""전체 테스트 세션에서 한 번만 생성"""
print("\n[session] setup")
config = {"env": "test", "debug": True}
yield config
print("\n[session] teardown")


def test_a(func_fixture, module_fixture, session_fixture):
func_fixture["count"] += 1
module_fixture["calls"] += 1
assert func_fixture["count"] == 1 # 항상 1
assert session_fixture["env"] == "test"


def test_b(func_fixture, module_fixture, session_fixture):
func_fixture["count"] += 1
module_fixture["calls"] += 1
assert func_fixture["count"] == 1 # 새 인스턴스이므로 1
assert module_fixture["calls"] == 2 # 누적

fixture 의존성 — fixture 가 fixture 를 사용

import pytest


@pytest.fixture
def base_config():
return {"host": "localhost", "port": 5432}


@pytest.fixture
def db_url(base_config):
"""base_config fixture를 주입받아 사용"""
host = base_config["host"]
port = base_config["port"]
return f"postgresql://{host}:{port}/testdb"


@pytest.fixture
def app_config(base_config, db_url):
"""여러 fixture 의존"""
return {
**base_config,
"db_url": db_url,
"debug": True,
}


def test_db_url(db_url):
assert "localhost" in db_url
assert "5432" in db_url


def test_app_config(app_config):
assert app_config["host"] == "localhost"
assert "postgresql" in app_config["db_url"]
assert app_config["debug"] is True

conftest.py — 공유 fixture

# tests/conftest.py — 자동으로 로드됨 (import 불필요)

import pytest


@pytest.fixture
def admin_user():
"""모든 테스트 파일에서 사용 가능"""
return {"id": 1, "name": "Admin", "role": "admin"}


@pytest.fixture
def regular_user():
return {"id": 2, "name": "User", "role": "user"}


@pytest.fixture(scope="session")
def app_settings():
"""세션 전체에서 공유"""
return {
"secret_key": "test-secret",
"database_url": "sqlite:///:memory:",
"testing": True,
}


# tests/test_auth.py
def test_admin_role(admin_user):
assert admin_user["role"] == "admin"


def test_user_role(regular_user):
assert regular_user["role"] == "user"


def test_app_in_test_mode(app_settings):
assert app_settings["testing"] is True

fixture 파라미터화

import pytest


# params로 여러 값을 자동 테스트
@pytest.fixture(params=["sqlite", "postgresql", "mysql"])
def database(request):
"""각 DB 타입으로 테스트를 3회 실행"""
db_type = request.param
print(f"\n[Setup] {db_type} 연결")
conn = {"type": db_type, "connected": True}
yield conn
print(f"\n[Teardown] {db_type} 연결 해제")


def test_db_connection(database):
# sqlite, postgresql, mysql 각각 실행됨
assert database["connected"] is True
assert database["type"] in ["sqlite", "postgresql", "mysql"]


# ids로 테스트 이름 지정
@pytest.fixture(
params=[
(1, 1, 2),
(0, 0, 0),
(-1, 1, 0),
],
ids=["positive", "zero", "mixed"],
)
def add_case(request):
a, b, expected = request.param
return {"a": a, "b": b, "expected": expected}


def test_add(add_case):
result = add_case["a"] + add_case["b"]
assert result == add_case["expected"]

내장 fixture

import pytest


# tmp_path: 임시 디렉토리 (자동 정리)
def test_write_read(tmp_path):
file = tmp_path / "data.txt"
file.write_text("Hello pytest")
assert file.read_text() == "Hello pytest"


# tmp_path_factory: 스코프 있는 임시 디렉토리
@pytest.fixture(scope="module")
def shared_dir(tmp_path_factory):
d = tmp_path_factory.mktemp("shared")
(d / "config.json").write_text('{"key": "value"}')
return d


def test_shared_config(shared_dir):
config = (shared_dir / "config.json").read_text()
assert "key" in config


# capsys: print 출력 캡처
def test_print_output(capsys):
print("Hello")
print("World", end="")
captured = capsys.readouterr()
assert captured.out == "Hello\nWorld"


# caplog: 로그 캡처
import logging


def test_logging(caplog):
with caplog.at_level(logging.WARNING):
logging.warning("경고 메시지")
logging.error("에러 메시지")

assert "경고 메시지" in caplog.text
assert len(caplog.records) == 2


# monkeypatch: 객체·환경변수 임시 교체
import os


def get_env_value():
return os.environ.get("MY_VAR", "default")


def test_env_patch(monkeypatch):
monkeypatch.setenv("MY_VAR", "test_value")
assert get_env_value() == "test_value"
# 테스트 종료 후 자동 복원


# request: fixture 메타데이터 접근
@pytest.fixture
def current_test(request):
return request.node.name


def test_knows_its_name(current_test):
assert current_test == "test_knows_its_name"

정리

개념설명
@pytest.fixturefixture 정의
yieldsetup/teardown 분리
scopefunction / class / module / session
conftest.py공유 fixture 파일
paramsfixture 파라미터화
tmp_path자동 정리 임시 디렉토리
monkeypatch런타임 객체 교체
capsys / caplog출력·로그 캡처

fixture는 반복되는 준비 코드를 제거하고 테스트를 독립적으로 유지하는 핵심 도구입니다.

Advertisement