kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Python + unittest を使ってテストパターンをパラメータ化するときに subTest() メソッドを使う

Python + unittest を使って TDD (Test Driven Development) の練習(ペアプログラミング)をしていたときに,リファクタリングのサイクルでテストパターンをパラメータ化することになった.Parameterized Test にリファクタリングをするために,期待値をパラメータ化し,for の中に assert を実装した.以下はサンプルコードとなり,インプット文字列を大文字に変換する挙動を確認している.さらに「意図的に」ce を誤った期待値に設定し,失敗するようにした.なお,今回は 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 のドキュメントにも「サブテストを利用して繰り返しテストの区別を付ける」として,サンプルコードが載っている.

docs.python.org

実際に subTest() メソッドを使ってリファクタリングをすると,以下のような実装になる.subTest() メソッドの定義は subTest(msg=None, **params) となり,msgparams にサブテスト失敗時のメタデータを設定できる.失敗時の判断を正確にするために重要なオプションと言える.今回はサンプルとして仕様を書いた.

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

github.com