Python|unittestでテスト構造を整理する考え方
この記事のポイント
- unittestのテストクラス設計をレビュー視点で整理できる
- フィクスチャ責任の整理技法を学べる
- 実務でのテスト構造整理の成長ステップを体系化できる
- PlantUMLでテスト構造肥大化と整理イメージを可視化できる
そもそもunittestとは
Python標準ライブラリのunittestはxUnit系統のテストフレームワークで、標準機能のみで次のようなテストが実現できます。
- テストケースのクラス構造化(TestCase)
- アサーションAPI提供(assertEqual、assertRaises等)
- フィクスチャ機能(setUp/tearDown)
- テストディスカバリ標準搭載
unittest基本例
import unittest
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(1 + 2, 3)
if __name__ == "__main__":
unittest.main()なぜこれをレビューするのか
unittest設計は「初期は手軽、肥大後に崩壊しやすい」構造を持ちます。レビューアーは以下の視点を持つ必要があります。
レビューアー視点
- テストクラス設計が責務分離できているか
- フィクスチャ(setUp/tearDown)が膨張していないか
- テスト名・粒度が読み取り可能か
- 依存順序が発生していないか
- テスト独立性が維持できているか
開発者視点
- 手軽に書き始められるためスコープ拡大を軽視しがち
- テストクラスに複数責務混在させがち
- setUp肥大を後追い整理できなくなる
- テスト依存順序を発生させがち
テスト構造崩壊の典型設計臭
崩壊しやすいパターン
- テストクラス1つに機能横断のテストを集中
- setUpで全フィクスチャ一括初期化
- 名前付けが「test_正常系」「test_異常系」レベル
- サブテストの過剰使用で構造が隠蔽
- テスト順序依存で副作用発生
崩壊例:フィクスチャ集中型
setUp肥大化例
import unittest
class TestUserService(unittest.TestCase):
def setUp(self):
self.db = MockDatabase()
self.user_service = UserService(self.db)
self.mailer = MailerMock()
self.payment_gateway = PaymentGatewayMock()
self.logger = LoggerMock()
def test_register_user(self):
result = self.user_service.register("alice")
self.assertTrue(result)
def test_payment_process(self):
result = self.payment_gateway.charge("alice", 100)
self.assertTrue(result)
@ReviewersetUpに全機能依存フィクスチャが混在しています。責務毎にクラス分離整理してください。
問題点
- テストクラスが複数責務を同居
- setUpで不要フィクスチャも毎回作成
- 読解コストと保守性悪化
崩壊構造モデル:肥大集中構造
良い設計整理アプローチ
unittestの整理文化は以下の段階が有効です。
① 責務別テストクラス分離
- 機能単位・API単位でクラス分離
② 最小フィクスチャ原則
- setUpは当該テストクラス最小単位で限定初期化
③ 明示命名ルール統一
- test_機能_条件_期待結果 の3要素命名
④ サブテスト最小利用文化
- テーブルテストは補助、設計臭の隠蔽に利用しない
改善例:責務分離版
責務別整理版
import unittest
class TestUserRegistration(unittest.TestCase):
def setUp(self):
self.db = MockDatabase()
self.user_service = UserService(self.db)
def test_register_user_success(self):
result = self.user_service.register("alice")
self.assertTrue(result)
def test_register_user_duplicate(self):
self.user_service.register("alice")
with self.assertRaises(UserAlreadyExistsError):
self.user_service.register("alice")
class TestPaymentProcessing(unittest.TestCase):
def setUp(self):
self.payment_gateway = PaymentGatewayMock()
def test_payment_success(self):
result = self.payment_gateway.charge("alice", 100)
self.assertTrue(result)「テストはクラスで分ける文化・フィクスチャを最小にする文化」
レビューアーはsetUp読解コストゼロ文化を常に推進すると健全性が維持できます。
改善構造モデル:責務分離整理
良くない実装例: ケース1(テーブル隠蔽依存)
サブテスト依存例
def test_user_registration(self):
for name in ["alice", "bob", "charlie"]:
with self.subTest(name=name):
result = self.user_service.register(name)
self.assertTrue(result)
@Reviewerサブテスト乱用で失敗粒度が読みにくくなります。個別テスト分離を優先してください。
問題点
- 失敗時原因特定コスト上昇
- レビュー時の仕様判読困難化
良くない実装例: ケース2(依存順序型)
順序依存例
def test_create_user(self):
self.user_service.create("alice")
def test_duplicate_user(self):
with self.assertRaises(UserAlreadyExistsError):
self.user_service.create("alice")
@Reviewerテスト間順序依存が発生しています。事前状態初期化で完全独立化してください。
問題点
- 実行順序依存バグ
- 並列実行破綻
改善例:初期化整理版
完全独立版
def test_duplicate_user(self):
self.user_service.create("alice")
with self.assertRaises(UserAlreadyExistsError):
self.user_service.create("alice")- 各テストで状態初期化明示
- 実行順序非依存
観点チェックリスト
まとめ
unittestの設計は「初期は楽、肥大は地獄」という典型構造を持っています。
レビューアーは「責務分離文化・フィクスチャ最小文化・完全独立文化」を徹底教育することで、長期保守に耐える健全テスト設計を支援できます。

