factory-boy
のようなツールを使うと、テストデータの準備は非常に簡単になります。しかし、強力なツールを手にしたからといって、無計画にデータを作成してはなりません。テストの品質は、そのテストが**「何を検証しようとしているのか」**がどれだけ明確であるかに大きく依存します。
そして、その意図を最も明確に表現するのが、テストのために準備される**「データ」**そのものです。
今回のテーマは、**「テストのノイズを減らし、シグナルを強める」**ためのデータ準備の原則です。つまり、検証したい内容に直接関係する「必要十分」なデータだけを用意することで、テストの可読性と信頼性をいかに向上させるかを解説します。
問題点:「ノイズ」が多く、意図が不明瞭なテストデータ
あるECサイトの商品検索機能search_products
をテストするシナリオを考えてみましょう。この関数は、検索語に加えて、オプションでカテゴリによる絞り込みも可能です。
悪い例:テストの関心事と無関係なデータ(ノイズ)が多い
# tests/test_search.py
from .factories import CategoryFactory, ProductFactory
from .search import search_products
def test_search_by_keyword():
# Arrange:
# このテストはキーワード検索を検証したいだけなのに、
# カテゴリ名まで細かく指定しているのはノイズ
category = CategoryFactory(name="PC周辺機器")
product1 = ProductFactory(name="静音ワイヤレスマウス", category=category)
# このテストでは使われない商品データも作られている
product2 = ProductFactory(name="4Kモニター", category=category)
# Act
results = search_products(term="マウス")
# Assert
# 1件見つかることはわかるが、なぜproduct2が除外されたかは自明ではない
assert len(results) == 1
assert results[0] == product1
このテストの問題点は、その**「ノイズの多さ」**にあります。
- 意図の不明瞭さ: このテストの目的は「”マウス”という単語で検索できること」のはずです。しかし、セットアップコードには
category
や"4Kモニター"
といった、この目的とは直接関係のない情報が含まれており、テストの核心(シグナル)が何なのかが分かりにくくなっています。 - 検証の甘さ: このテストは、1件だけ正しく見つかるケースしか検証していません。「マウス」という単語を含む商品が複数あった場合に、それらすべてを正しく見つけ出せるかは保証されていません。
解決策:「必要十分」なデータでテストの物語を語る
優れたテストは、その準備データを通じて「何を検証したいのか」という物語を明確に語ります。
ケース1:キーワード検索のテスト
キーワード検索をテストするなら、必要なデータは「キーワードを含む商品」「含まない商品」そして「キーワードを複数含む商品」です。
良い例:テストの意図に合わせてデータを厳選
def test_search_by_keyword():
# Arrange: 各データが明確な役割を持つ
product_to_find_1 = ProductFactory(name="静音ワイヤレスマウス")
product_to_find_2 = ProductFactory(description="高性能なゲーミングマウスです。")
product_to_ignore = ProductFactory(name="メカニカルキーボード")
# Act
results = search_products(term="マウス")
# Assert: 期待する2件が漏れなく、かつ余計な1件が含まれないことを検証
assert len(results) == 2
# 順序を問わないようにsetで比較するのが堅牢
assert set(results) == {product_to_find_1, product_to_find_2}
このテストは、変数名 (_to_find
, _to_ignore
) とデータの作り方によって、その意図を明確に物語っています。また、複数件ヒットするケースを検証することで、テストの信頼性も向上しています。
ケース2:キーワード+カテゴリ検索のテスト
カテゴリによる絞り込みをテストするなら、必要なデータは「キーワードとカテゴリが両方一致する商品」「キーワードは一致するがカテゴリが違う商品」「カテゴリは一致するがキーワードが違う商品」です。
良い例:条件の組み合わせを検証するためにデータを設計
def test_search_by_keyword_and_category():
# Arrange:
# factory-boyの `__` 記法で関連モデルの属性も簡潔に指定
product_to_find = ProductFactory(name="多機能マウス", category__name="PCアクセサリ")
product_to_ignore1 = ProductFactory(name="ワイヤレスマウス", category__name="オーディオ機器")
product_to_ignore2 = ProductFactory(name="トラックボール", category__name="PCアクセサリ")
# Act
results = search_products(term="マウス", category_name="PCアクセサリ")
# Assert
assert len(results) == 1
assert results[0] == product_to_find
このテストデータは、「”PCアクセサリ” カテゴリの “マウス”」という条件を検証するために、必要最小限かつ十分な対照群を用意しており、テストのロジックが非常に明確です。
まとめ
テストの品質を高めるためのデータ準備の指針は以下の通りです。
- ノイズをなくす: テストの検証内容と無関係なデータや属性の指定をやめる。
- シグナルを強める: 検証したい振る舞いに直接関わるデータは、ファクトリの引数で明示的に指定する。
- 対照実験を行う: 条件に「一致するデータ」と「一致しないデータ」の両方を用意し、フィルタリングが正しく機能することを保証する。
- 複数件をテストする: 複数件の結果が返りうる機能では、実際に複数件のマッチングデータを用意して検証する。
テストデータは単なる「準備」ではありません。それは、テストの意図と仕様を伝えるための最も雄弁な「物語」なのです。