※ 注意
これは「ユニットテストはこういうことを書くのがいいよ!」という話ではありません。「フル自作より、フレームワーク使った方が良いよ」という話です。
良いユニットテストのやり方については、別の解説を探してください。
背景
(恥ずかしながら、真面目にユニットテストをするようになったのは割と最近のお話。)
テストの習慣化を始めたそのときは、テストフレームワークという言葉は聞いたことあったが、まともに向き合っていなかった。
で、あるとき「いい加減フレームワークの一つくらい触れておこうか・・・」と思い、とりあえず Python 標準のユニットテストフレームワーク unittest
を使ってみた。
すると、これまで書いていたテストコードより、明らかに書くコード量が減ったではありませんか。なぜ今まで向き合わなかったのか・・・。
というわけで、その感動そのままに、本記事執筆に至りました。
ユニットテストとは (軽く)
単体テストとも。
単体とは、例えば自作関数 1つとか、プログラムが機能するレベルとして最低規模と考えればヨシ。
テストとは、試験、つまりそのプログラムが想定 (設計・仕様) 通りに動くかどうかチェックすること。
要するに、プログラムの小規模パーツ単位でのチェック。より詳しいことは解説がたくさんあるのでカット。
ユニットテストコード例
足し算と引き算のテストをするとします。ここで、引き算のテストには、あえて誤りを入れています。
def expect_equal(expected, actual):
if expected == actual:
print("--- OK")
else:
print("--- NG")
print(f"Expected: {expected}")
print(f"Actual : {actual}")
def test_add():
print("-- test_add")
expected = 3
actual = 1 + 2
expect_equal(expected, actual)
def test_sub():
print("-- test_sub")
expected = 2
actual = 5 - 2 # 何か不具合があったとする
expect_equal(expected, actual)
if __name__ == "__main__":
test_add()
test_sub()
python3 test_code_original.py
-- test_add
--- OK
-- test_sub
--- NG
Expected: 2
Actual : 3
unittest
使用で書き直すと
全く同じテストを、今度は unittest
使用で書いてみます。
import unittest
class test_add_sub(unittest.TestCase):
def test_add(self):
expected = 3
actual = 1 + 2
self.assertEqual(expected, actual)
def test_sub(self):
expected = 2
actual = 5 - 2 # 何か不具合があったとする
self.assertEqual(expected, actual)
if __name__ == "__main__":
unittest.main()
python3 test_code_unittest.py
.F
======================================================================
FAIL: test_sub (__main__.test_add_sub)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/<path>/test_code.py", line 12, in test_sub
self.assertEqual(expected, actual)
AssertionError: 2 != 3
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=1)
-v
オプションを付けると、実行中テスト名を詳しく出してくれます。
python3 test_code_unittest.py -v
test_add (__main__.test_add_sub) ... ok
test_sub (__main__.test_add_sub) ... FAIL
======================================================================
<以降同じ>
テストフレームワークのここが良い
上 2つのコードとテスト結果出力を比較すると、unittest
で以下の恩恵が得られると言えそうです。
- 実行中テスト名を自分で出力しなくてよい
- テスト結果を自分で出力しなくてよい
- テスト関数の呼び出しを書かなくてよい
-
unittest.main()
だけで OK - テスト関数の定義を書く・消すだけ
-
- 失敗したテストを詳細に出力してくれる
- テスト名、結果値の違い、該当の行番号
加えて unittest
は Python 標準なので、追加の準備が不要という利点もあります。
ここでは Python の unittest
を例にしましたが、cpputest など他のフレームワークも、上のような特徴を持っていることでしょう。
まとめ
ユニットテストフレームワークを導入すると、面倒なコードを書くことなく、テストをサクッと実装できて良い。
「結果出力書くの面倒でテスト書く気起きん」というような人が居たら、早急にフレームワーク導入を勧めましょう。
あと、テストはやるべきというのは言わずもがな。