unittest で Python の単体テストをする
unittest を使って Python の単体テストをする方法のメモ。
テスト対象コード
# -*- coding:utf-8 -*- # sample.py def fizzbuzz(number: int) -> str: if type(number) != int: raise TypeError("number is integer type.") if number % 15 == 0: ret = "fizzbuzz" elif number % 3 == 0: ret = "fizz" elif number % 5 == 0: ret = "buzz" else: ret = str(number) return ret
テストコードの書き方
テストコードを記載したモジュールは test_sample.py とする。
まず、unittest とテスト対象のモジュール等をインポートする。
import unittest from sample import fizzbuzz # テスト対象
次に、テスト対象となるモジュールに対して、unittest.TestCase を継承したテスト用クラスを記述する。
class TestSample(unittest.TestCase): def test_fizzbuzz1(self): self.assertEqual(fizzbuzz(1), "1") def test_fizzbuzz2(self): self.assertEqual(fizzbuzz(3), "fizz") ........ def test_fizzbuzz5(self): with self.assertRaises(TypeError): fizzbuzz(1.)
その後、コマンドラインで以下を実行する。
python -m unittest test_sample
コマンドが実行されると、 unittest.TestCase を継承したクラスの test_ から始まるメソッドをテストコードとしてテストが実行される。
それぞれの実行結果が正しいか否かは assertEqual や assertRaises などの結果をテスト結果としてコマンドライン上に出力する。*1
上のコマンドの実行結果は下のようになり、テストに成功していることがわかる。
..... ---------------------------------------------------------------------- Ran 5 tests in 0.001s OK
また、
ret = "fizzbuzz" -> ret = "fizbuz"
の様に sample.py を書き換えてテストを実行すると
F.... ====================================================================== FAIL: test_fizzbuzz1 (test_sample.TestSample) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\Users\user\work\python_unittest_sample\test_sample.py", line 9, in test_fizzbuzz1 self.assertEqual(fizzbuzz(1), "1") AssertionError: 1 != '1' ---------------------------------------------------------------------- Ran 5 tests in 0.002s FAILED (failures=1)
となり、test_fizzbuzz1に失敗したことが表示される。
また、実行前に行う処理はsetUp() メソッド, 実行後に行う処理は tearDown() メソッドに記載することが出来る。
複数の処理を実行する場合
一般的に、メソッドは分岐を持つ場合がほとんどであり、それらを上の様に各メソッドに実装するとコードが長くなり面倒くさいし、テストコードにバグが紛れ込む可能性がある。
これらを防ぐために、subTest() を用いることでいくつかのテストをまとめて行うことが出来る。*2
上の備考を参考に test_sample.py を書き換えたものが下のコードだ。正常系と異常系をどのように実装するのが適切なのかは今後調べる必要がありそうだ、、、
# -*- coding:utf-8 -*- import unittest from sample import fizzbuzz class TestSample(unittest.TestCase): def test_fizzbuzz_normal(self): test_case = [ (1, "1"), (3, "fizz"), (5, "buzz"), (15, "fizzbuzz") ] for param, ret in test_case: with self.subTest(number=param): self.assertEqual(fizzbuzz(param), ret) def test_fizzbuzz_error(self): with self.assertRaises(TypeError): fizzbuzz(1.)
*1:assertEqual, assertRaises 以外のメソッドはunittest --- ユニットテストフレームワーク — Python 3.8.6rc1 ドキュメントを参照