Writer: yasuhara 更新日:2023/07/26
現在のプロジェクトで初めてpytestを使用したテストの自動化を行ったので、備忘録としてまとめます。
pytestを使う理由
一口にPythonのテストツールといっても現在いろいろなツールがある。
今回pytestを使用する理由を、例としてPython標準ツールのunittestと比較してみる
その1.直観的に書ける
pytestはassertの後に条件式を書けば結果の検証ができるが
unittestは複数種類のassert関数を覚える必要がある
その2.Classを書かなくていい
unittestはテスト用のClassを作成する必要があるが
pytestはtest_*.pyまたは*_test.pyという名前のファイルに、test_*という関数名を付けるとpytestがテスト関数だと判断してくれる
その3.テストの失敗理由が分かりやすい
unittestどのテストで失敗したかはわかるがテスト中のどの部分で失敗したか、なぜそのテストが失敗したかが示されない
pytestは失敗した箇所のどの値がなぜダメだったかをわかりやすく表示してくれる
assert
assertの後に検証したい内容を書く
引数a,bを足した値を返す関数add_funcをテストしたいとすると
1 2 3 4 5 |
def add_func(a, b): return a + b def test_add_func(): assert add_func(1, 2) == 3 |
add_func(1, 2)の返り値は3が返ってきて、期待通りの値が返ってきてテストをパスする。
一方で
1 2 3 4 5 |
def add_func(a, b): return a - b def test_add_func(): assert add_func(1, 2) == 3 |
のようなコードだとadd_func(1, 2)の返り値は-1となり、返り値が期待通りでないのでテストをパスできず、add_funcの実装が間違っていることが分かる。
parametrize
次に引数aから引数bを引いた値を返す関数sub_funcのテストを考える
この場合テストパターンとして(a>b, a=b, a<b)の3通りのテストをしたいとする
1 2 3 4 5 6 7 |
def sub_func(a, b): return a-b def test_sub_func() assert sub_func(2, 1) == 1 assert sub_func(2, 2) == 0 assert sub_func(1, 2) == -1 |
としてもよいのだがテスト関数の部分は
1 2 3 4 5 6 7 |
@pytest.mark.parametrize('a, b, result', [ (2, 1, 1), (2, 2, 0), (1, 2, -1) ]) def test_sub_func(a, b, result): assert sub_func(a, b) == result |
と書くことができる。
3通りぐらいなら上の書き方でもよいのだがテストパターンが増えてくると、parametrizeの第二引数を別のファイルで定義して渡すとよい
freeze_time
現在の日付の1週間前の日付を返す関数をテストすることを考える
1 2 3 4 |
from datetime import datetime, timedelta def get_one_week_ago(): return datetime.now() - timedelta(weeks=1) |
テストしたい関数の中でdatetime.datetime.now()を扱う場合、テストを行う日時によってnow()の中身が変動してしまう。
そうすると出力される値が特定できないためテストができなくなってしまう。
そこでfreeze_timeを使うとテストを実行する日時を固定することができる
1 2 3 |
@pytest.mark.freeze_time(datetime(2023, 6, 30)) def test_get_one_week_ago(): assert get_one_week_ago == datetime(2023, 6, 23) |