Python - pytest:pytestを用いてテストをする
初めに
Pythonでテストを作成するとき、pytestと呼ばれるモジュールを使用します。本記事はpytestの使い方を簡単にまとめます。
pytest
使い方
ソースコードが1つの場合
テストしたいソースコードが1つの場合から始めます。
テストしたいソースコードと同じフォルダに、テスト用の .py ファイルを作成します。
今回は簡単な足し算をするための関数をテストします。
# code1.py def sum_num(x, y): return x + y
import code1 # code1.py の sum_cum() テスト用関数 def test_sum_num(): result = code1.sum_num(2, 3) assert result == 5 # 正しい結果
テストを実行するには、TERMINALでpytest ファイル名
というコマンドを使います。
(今回のコマンド)
pytest test_code.py
テストの結果が表示されます。今回は1つのテストが合格していることが分かります。
仮にassert result == 3
と書き換えてテストを実行してみると次のようになります。FAILUREと表示されている部分にエラーが起こった関数が表示されます。FAILEDの部分に具体的に誤りがあった部分が表示されます。
テストをする際は、あらゆる場合を想定しどのような場合でも変な挙動をしないか確認する必要があります。そのため、次のように場合を増やし、どのような場合でも正しい処理がされるか確認します。
import code1 def test_sum_num(): assert code1.sum_num(1, 2) == 3 assert code1.sum_num(0, 4) == 4 assert code1.sum_num(2, -2) == 0 assert code1.sum_num(-5, 3) == -2 assert code1.sum_num(23, 77) == 100 assert code1.sum_num(-10, -4) == -14
※実際には、上のテストは不十分で float 型の場合などが考慮できていません。
パラメータ化したテスト
上記のように毎回、assert
などと記述するのは冗長であるため、パラメータ化することを考えます。
テストで使用するパラメータは@pytest.mark.parametrize()
デコレータを用い、記述します。
試しに、次の引数で与えられた数字が素数かどうか判定する関数をパラメータ化したテストで検証してみます。
# code1.py def is_prime(n): """ 引数が素数かどうかを判定する関数。 Args: n: 判定対象の数 Returns: n が素数であれば True、そうでなければ False """ if n <= 1: return False for i in range(2, int(n**0.5) + 1): if n % i == 0: return False return True
次がテスト用コードです。
# code_test.py import pytest import code1 # テスト用パラメータ @pytest.mark.parametrize(("num", "expected"), [ (1, False), (2, True), (3, True), (4, False), (5, True), (6, False), (7, True), (8, False), (9, False), (10, False) ]) def test_sum_num(num, expected): assert code1.is_prime(num) == expected
@pytest.mark.parametrize()
デコレータを使うために、pytestをインポートしています。テスト用のパラメータを作り、そのパラメータの値をテストに使っています。@pytest.mark.parametrize()
で指定する引数は、テスト用関数の引数になります。
モック
モックは、テスト対象のコードで実際に使用されるオブジェクトの変わりに使用する偽にオブジェクトです。
モックは、以下のような場合に使用されます。
- テスト対象のコードが依存している外部ライブラリやサービスがテスト時に利用できない場合
- テスト対象のコードの特定の動作のみを検証したい場合
- テスト対象のコードの挙動を制御して、様々なケースを想定したテストを書きたい場合
(テストしたい関数)
def get_name(): """ユーザーから名前を入力し、返す関数です。 Returns: str: 入力された名前。 """ name = input("名前を入力してください: ") return name def get_name_length(): name = get_name() return len(name)
今回は入力された名前を返す関数を呼び出し、その名前の文字数を返す関数get_name_length()
をテストします。これはテスト段階では、get_name()
が実行できないため、モックを使い、テストをする必要があります。
モックを使うには、pytest-mock モジュールをインストールする必要があります。TERMINALで次のコマンドを実行しインストールします。
pip install pytest pytest-mock
モックオブジェクトを生成するにはmocker.patch()
を使います。
import pytest import code1 def test_get_name_length(mocker): # モックオブジェクトを生成 # モックにする関数を引数にする mock_get_name = mocker.patch("code1.get_name") # モックオブジェクトの設定 # 返り値を指定 mock_get_name.return_value = "山本源流斎重国" # テスト対象の関数を実行 result = code1.get_name_length() # 検証 assert result == 7
モックオブジェクトの設定は次のように返り値の設定も合わせた形で記述することもできます。
mocker.patch("code1.get_name", return_value = "山本源流斎重国")
モックオブジェクトが呼ばれた回数を検証することが可能です。下記のようなコードをテスト用関数の最下部に書くことで検証できます。
# テスト用関数の最下部に記述 # m はモックオブジェクト assert m.call_count == 3 # call_countで呼ばれた回数を取得 m.assert_called() # 少なくても1度は呼ばれた事の検証 m.assert_called_once() # 1回だけ呼ばれた事の検証 m.assert_not_called() # 1回も呼ばれていない事の検証