L?? ch02 — pytest fixture
テクノロジ系 / L10 test — テスト
L?? ch02 — pytest fixture
テストの「準備」を関数化して、毎回書かない・壊れにくくする
ゴール
@pytest.fixtureで「テスト前の準備」を共通化できる- テスト関数の引数名 = fixture 名 で受け取れる
scopeの違い (function / module) を最低限知っている
なぜ fixture か (Before)
def test_user_name():
user = {"name": "Taro", "age": 30} # 準備
assert user["name"] == "Taro"
def test_user_age():
user = {"name": "Taro", "age": 30} # ← 同じ準備をまた書く
assert user["age"] == 30「準備」がコピペになり、データ変更で 複数箇所を直す ことに。
After (fixture でまとめる)
import pytest
@pytest.fixture
def user():
return {"name": "Taro", "age": 30}
def test_user_name(user): # 引数名 = fixture 名
assert user["name"] == "Taro"
def test_user_age(user):
assert user["age"] == 30user は テストごとに毎回呼ばれる (= 独立した状態)。
fixture の最小ルール
| ルール | 内容 |
|---|---|
| 1 | @pytest.fixture を付けた関数を作る |
| 2 | テスト関数の 引数名 に fixture 名を書く |
| 3 | return した値が、その引数として渡される |
| 4 | fixture は fixture を引数に取れる (連鎖) |
fixture の連鎖
@pytest.fixture
def base_user():
return {"name": "Taro"}
@pytest.fixture
def admin_user(base_user):
return {**base_user, "role": "admin"}
def test_admin(admin_user):
assert admin_user["role"] == "admin"
assert admin_user["name"] == "Taro" # base から継承scope の違い
@pytest.fixture # 既定 = function (テストごと)
def fresh_user():
return {"count": 0}
@pytest.fixture(scope="module") # モジュール単位で 1 回だけ
def db_connection():
return connect_db() # 重い処理を共有function(既定): 安全。テスト独立。module: 速い。状態共有に注意 (テスト順依存になりがち)。
後片付け (yield)
@pytest.fixture
def temp_file(tmp_path):
f = tmp_path / "data.txt"
f.write_text("hello")
yield f # ← ここまでが「準備」
# ↓ テスト後に実行される (= 後片付け)
if f.exists():
f.unlink()return の代わりに yield を使うと、テスト後処理が書ける。
よくある間違い
# ❌ fixture を「関数呼び出し」してしまう
def test_user():
u = user() # → fixture は直接呼べない (pytest が injection する)
# ❌ 引数名が fixture 名と違う
def test_user(usr): # → fixture "usr" が無い と怒られる
...
# ❌ fixture 内で global を書き換える
@pytest.fixture
def bad():
global COUNTER # → テスト間で状態漏れるdrill
drill/ — user fixture を完成させて、テストを PASS させる
拡張課題
- conftest.py に切り出す: 複数テストファイルで使う fixture は
conftest.pyに置けば import 不要で共有される - parametrize と組み合わせる:
@pytest.mark.parametrizeでテストデータを増やしつつ fixture で前処理 - FastAPI の TestClient を fixture 化:
clientfixture を作っておくと、各エンドポイントテストが 1 行で書ける
@pytest.fixture
def client():
from fastapi.testclient import TestClient
from app.main import app
return TestClient(app)
よくある詰まり
| 症状 | 原因 | 対処 |
|---|---|---|
fixture 'xxx' not found | 引数名と fixture 名が不一致 | スペルを揃える |
| テストごとに値が変わらない | scope="module" で状態が漏れている | scope を function に戻す |
yield の後が実行されない | テスト中に例外が出ても後処理は走るのが正しい挙動。走らないなら fixture の登録漏れ | @pytest.fixture を付けたか確認 |
次に学ぶ領域
| 領域 | 続けて読む |
|---|---|
| FastAPI の TestClient | test/ch03-testclient (次 chapter) |
| conftest.py での共有 | pytest 公式 docs |
| モック (unittest.mock) | test/ch04-mock (将来) |
力試ししたい方は クイズ形式の演習(任意) もあります。読むだけでも十分です。