PythonにおけるユニットテストのためのPyTestの使い方

コードをPythonで書くときは、コードが期待通りに動作することを確認することが重要です。これを行う最良の方法の一つはユニットテストを使用することで、コードの小さな部分(またはユニット)が正しく機能しているかを確認するのに役立ちます。

この記事では、PyTestを使用して、Pythonで効果的なユニットテストを書く方法と実行する方法を学びます。

ユニットテストとは何か?

ユニットテストは、単一の関数や小さなコードの断片をチェックすることに焦点を当てた小さくシンプルなテストです。これにより、コードが期待通りに動作することを保証し、早期にバグを捕まえることができます。

ユニットテストは、関数、メソッド、さらにはクラスなど、コードのさまざまな部分に対して書くことができます。ユニットテストを書くことで、プログラム全体を実行せずにコードをテストできます。

なぜPyTestを使用するのか?

PyTestは、テストを書くことと実行することを容易にするPythonの人気のあるテストフレームワークです。

使いやすく、以下のような多くの便利な機能があります:

  • シンプルで明確なテストケースを書くことができます。
  • フィクスチャ、パラメータ化テスト、プラグインなどの高度な機能を提供します。
  • 他のテストツールやライブラリとうまく連携します。
  • 読みやすいテスト結果とレポートを生成します。

LinuxでのPyTestの設定

テストを書く前に、PyTestをインストールする必要があります。もしPyTestがインストールされていない場合は、Pythonパッケージマネージャーのpipを使ってインストールできます。

pip install pytest

PyTestがインストールされたら、テストを書く準備が整いました!

PyTestでの最初のテストを書く

まずは、簡単な関数を書き、それに対するテストを書きましょう。

ステップ1: 簡単な関数を書く

まず、テストしたいPython関数を作成します。二つの数を足す関数があるとしましょう:

# add.py
def add(a, b):
    return a + b

これは、二つの数aとbを受け取り、足し算をして結果を返す簡単な関数です。

ステップ2: 関数のテストを書く

次に、add関数のテストを書きます。PyTestでは、テストは通常test_*.pyという名前の別々のファイルに書かれ、テストファイルを特定しやすくしています。

test_add.pyという新しいファイルを作成し、次のテストコードを書きます:

# test_add.py
from add import add

def test_add_numbers():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

上記のコードの説明:

  • add関数をadd.pyファイルからインポートします。
  • test_add_numbers()というテスト関数を定義します。PyTestでは、テスト関数はtest_という言葉で始まるべきです。
  • テスト関数の中で、assert文を使用して、add関数を呼び出した結果が期待される値と一致するかどうかを確認します。assert文の条件がTrueの場合、テストは合格します。そうでない場合、テストは失敗します。

ステップ 3: テストの実行

テストを実行するには、ターミナルを開き、test_add.pyファイルがあるディレクトリに移動し、次のコマンドを実行します:

pytest

PyTestはすべてのテストファイル(test_で始まるファイル)を自動的に見つけて、その中のテストを実行します。すべてが正しく動作していれば、次のような出力が表示されます:

Verifying Python Code Functionality

ドット(.)は、テストが合格したことを示します。もし問題があった場合、PyTestはエラーメッセージを表示します。

より高度なテストの作成

基本的なテストの書き方と実行方法がわかったので、PyTestのより高度な機能を探ってみましょう。

期待される例外のテスト

時には、何かがうまくいかないときにコードが正しい例外を発生させるかどうかをテストしたいことがあります。これをpytest.raises()関数を使って行うことができます。

たとえば、2つの数を割る関数をテストしたいとします。2番目の数がゼロの場合(ゼロによる割り算エラーを避けるため)に例外を発生させたいとします。

こちらがdivide関数です:

# divide.py
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

次に、ゼロで割ろうとしたときにValueErrorが発生するかどうかを確認するこの関数のテストを書きましょう:

# test_divide.py
from divide import divide
import pytest

def test_divide_numbers():
    assert divide(10, 2) == 5
    assert divide(-10, 2) == -5
    assert divide(10, -2) == -5

def test_divide_by_zero():
    with pytest.raises(ValueError):
        divide(10, 0)

コードの説明:

  • 新しいテスト関数test_divide_by_zero()を追加しました。
  • この関数の中で、0を第二引数として除算関数を呼び出した際にValueErrorが発生するかどうかを確認するためにpytest.raises(ValueError)を使用しています。

pytestコマンドでテストを再実行します。すべてが正常に動作している場合、次の出力が表示されるはずです:

Test Your Code with PyTest

セットアップとクリーンアップのためのフィクスチャの使用

場合によっては、テストを実行する前に特定の条件を設定したり、テストが完了した後にクリーンアップする必要があることがあります。PyTestはこれを処理するためのフィクスチャを提供しています。

フィクスチャは、テストのために必要なオブジェクトを作成したり、データベースに接続したりするために使用できる関数です。

ファイル操作のテスト用に一時ディレクトリをセットアップするフィクスチャを使用する例を以下に示します:

# test_file_operations.py
import pytest
import os

@pytest.fixture
def temporary_directory():
    temp_dir = "temp_dir"
    os.mkdir(temp_dir)
    yield temp_dir  # This is where the test will run
    os.rmdir(temp_dir)  # Cleanup after the test

def test_create_file(temporary_directory):
    file_path = os.path.join(temporary_directory, "test_file.txt")
    with open(file_path, "w") as f:
        f.write("Hello, world!")
    
    assert os.path.exists(file_path)

コードの説明:

  • テストを行う前に一時ディレクトリを作成し、テスト後に削除するtemporary_directoryという名前のフィクスチャを定義しています。
  • テスト関数test_create_file()では、このフィクスチャを使用して一時ディレクトリにファイルを作成し、そのファイルが存在するかどうかを確認します。

pytestコマンドでテストを再実行します。PyTestはフィクスチャを自動的に検出して使用します。

Pytestを使用してテストをパラメーター化する時々、異なる入力で同じテストを実行したいことがあります。PyTestを使用すると、parametrizeを簡単に使用してこれを行うことができます。

数ペアで私たちのadd関数をテストしたいとしましょう。各ペアのために別々のテスト関数を書く代わりに、pytest.mark.parametrizeを使用して異なる入力で同じテストを実行できます。

# test_add.py
import pytest
from add import add

@pytest.mark.parametrize("a, b, expected", [
    (2, 3, 5),
    (-1, 1, 0),
    (0, 0, 0),
    (100, 200, 300)
])
def test_add_numbers(a, b, expected):
    assert add(a, b) == expected

コードの説明:

  • pytest.mark.parametrizeデコレーターを使用して、複数の入力セット(abexpected)を定義します。
  • テストfunction test_add_numbers()は、各入力セットごとに1回実行されます。

pytestコマンドでテストを再実行すると、各入力セットごとに1回、計4回テストが実行されます。

結論

この記事では、PythonPyTestを使用して効果的なユニットテストを作成し、実行する方法を学び、バグを早期に発見し、コードが期待どおりに動作することを確認しました。

PyTestはこれらのテストを簡単に作成および実行できるようにし、その強力な機能により、Pythonの旅が進むにつれてより複雑なテストニーズにも対応できます。

Source:
https://www.tecmint.com/unit-testing-python-code-with-pytest/