Python + unittest を使って TDD (Test Driven Development) の練習(ペアプログラミング)をしていたときに,リファクタリングのサイクルでテストパターンをパラメータ化することになった.Parameterized Test にリファクタリングをするために,期待値をパラメータ化し,for
の中に assert
を実装した.以下はサンプルコードとなり,インプット文字列を大文字に変換する挙動を確認している.さらに「意図的に」c
と e
を誤った期待値に設定し,失敗するようにした.なお,今回は Python 3.7 を検証環境にした.
import unittest class MyTestCase(unittest.TestCase): def test_upper(self): patterns = [ ('a', 'A'), ('b', 'B'), ('c', 'c'), ('d', 'D'), ('e', 'e'), ] for lower, upper in patterns: self.assertEqual(lower.upper(), upper)
テストコードを実行すると,以下の結果となる.期待した通りにテストは失敗しているけど,c
の失敗で止まってしまう.
AssertionError: 'C' != 'c' - C + c FAILED (failures=1)
subTest()
メソッドを使う
Python 3.4 で追加された subTest()
メソッドを使うと,テストパターンをサブテスト化して区別できるようになる.unittest のドキュメントにも「サブテストを利用して繰り返しテストの区別を付ける」として,サンプルコードが載っている.
実際に subTest()
メソッドを使ってリファクタリングをすると,以下のような実装になる.subTest()
メソッドの定義は subTest(msg=None, **params)
となり,msg
と params
にサブテスト失敗時のメタデータを設定できる.失敗時の判断を正確にするために重要なオプションと言える.今回はサンプルとして仕様を書いた.
import unittest class MyTestCase(unittest.TestCase): def test_upper(self): patterns = [ ('a', 'A'), ('b', 'B'), ('c', 'c'), ('d', 'D'), ('e', 'e'), ] for lower, upper in patterns: with self.subTest('%s is upper case of %s' % (upper, lower)): self.assertEqual(lower.upper(), upper)
テストコードを実行すると,以下の結果となる.
- サブテストを全て実行してから結果を表示できた(
e
の失敗まで実行できた) [e is upper case of e]
のように失敗時のメッセージを表示できたfailures
にサブテスト数を表示できた
AssertionError: 'C' != 'c' - C + c AssertionError: 'E' != 'e' - E + e One or more subtests failed Failed subtests list: [c is upper case of c], [e is upper case of e] FAILED (failures=2)
まとめ
- Python + unittest を使って Parameterized Test を実装するときは
subTest()
メソッドを使う - ドキュメントを読むと「サブテストをネストできる」と書いてあり,試してみようと思う
- Python 用に Parameterized というライブラリもあり,1度試してみようと思う