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度試してみようと思う