Traffine I/O

日本語

2023-02-17

pytest

pytest とは

pytestは、Pythonのテストフレームワークの1つで、Pythonのコードをテストするためのツールです。以下は、pytestの主な特徴についての説明です。

  • シンプルで使いやすい
    pytestは、簡潔で読みやすいコードでテストを記述することができます。また、テストの自動検出やパラメータ化などの機能があり、より簡単なテストの記述が可能です。

  • fixture
    pytestは、fixtureと呼ばれる機能を提供します。fixtureは、テストの前処理や後処理などを実行するための機能で、テストコードの重複を減らすことができます。

  • プラグインアーキテクチャ
    pytestは、プラグインアーキテクチャを採用しており、ユーザーが独自のプラグインを作成することができます。これにより、pytestの機能を拡張したり、自分のプロジェクトに適した機能を追加したりすることができます。

  • テストランナー
    pytestは、複数のテストを実行するためのランナーを提供します。テストランナーは、テストの収集、実行、レポートの生成などを行います。

  • 多様なテストスタイル
    pytestは、様々なテストスタイルに対応しています。例えば、unittestスタイルのテスト、BDDスタイルのテスト、doctestスタイルのテストなどに対応しています。

  • 多言語対応
    pytestは、Pythonだけでなく、他の言語のテストもサポートしています。例えば、CやC++ のテストを実行するためのプラグインがあります。

  • テストカバレッジ
    pytestは、テストカバレッジを測定するためのプラグインも提供しています。テストカバレッジとは、コードのどの部分がテストされたかを示す指標で、テストカバレッジが高いほど、コードの品質が高いとされています。

  • クラスやメソッドレベルのスコープ
    pytestは、テストのスコープを柔軟に設定することができます。例えば、クラスレベルのfixtureや、メソッドレベルのテストなどを定義することができます。

  • テストの並列実行
    pytestは、テストを並列実行することができます。これにより、テストの実行時間を短縮することができます。

  • 外部のテストランナーとの統合
    pytestは、外部のテストランナーと統合することができます。例えば、JenkinsやTravis CIなどのCI/CDツールと統合することができます。

  • パラメータ化テスト
    pytestは、パラメータ化テストをサポートしています。パラメータ化テストとは、同じテストを複数の入力値に対して実行することで、網羅的なテストを行う手法です。pytestでは、@pytest.mark.parametrizeデコレータを使用してパラメータ化テストを定義することができます。

  • テストのスキップやスキップ条件の設定
    pytestでは、テストをスキップすることができます。また、スキップ条件を指定することもできます。例えば、特定のOSやPythonバージョンでのみテストを実行する場合などに有用です。

  • フック関数
    pytestでは、テストの実行前や実行後にフック関数を実行することができます。これにより、テストの前処理や後処理を柔軟に設定することができます。

  • カスタムアサーション
    pytestでは、独自のアサーションを定義することができます。これにより、アプリケーションやライブラリの仕様に合わせたアサーションを定義することができます。

  • 優れたレポーティング
    pytestは、豊富なレポーティング機能を提供しています。テストの実行結果をHTML、XML、JSONなどの形式で出力することができ、視覚的にテスト結果を確認することができます。

pytestは、Pythonのアプリケーションやライブラリのテストに広く使用されており、多くの開発者によって支持されています。また、pytestは、他のPythonのテストフレームワーク(例えば、unittestやnose)との統合も可能です。

pytest の使い方

pytestを使ってPythonプロジェクトのテストを実行するには、以下の手順に従います。

  1. pytestをインストールする
  2. テストファイルを作成する
  3. テストを実行する
  4. オプションの使用

pytest をインストールする

以下のコマンドでpytestをインストールします。

bash
$ pip install pytest

テストファイルを作成する

テストファイルは、ファイル名が"test*"で始まり、.pyで終わる必要があります。また、テスト関数は、"test*"で始める必要があります。例えば、以下のようなテストファイルを作成します。

test_sample.py
def test_addition():
    assert 1 + 2 == 3

def test_subtraction():
    assert 5 - 3 == 2

テストを実行する

コマンドラインで、以下のようにpytestコマンドを実行します。pytestは、カレントディレクトリ以下の全てのテストファイルを自動的に検出して実行します。

bash
$ pytest

テストが正常に実行されると、以下のような出力が表示されます。

bash
============================= test session starts =============================
platform win32 -- Python 3.7.6, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: C:\projects\pytest
collected 2 items

test_sample.py ..                                                       [100%]

============================== 2 passed in 0.04s ==============================

この例では、2つのテストが実行され、全てが合格しました。

オプションの使用

pytestには、様々なオプションがあります。例えば、以下のようにオプションを指定して、レポート形式を変更することができます。

bash
$ pytest --junitxml=junit.xml

この例では、テスト結果をjunit.xmlというファイルに書き出します。このファイルは、CI/CDツールで利用することができます。

テストのパラメータ化

pytestでは、同じテストを複数回実行することができます。例えば、異なる引数を使用して、同じテストを複数回実行することができます。例えば、以下のようにして、異なる引数を使用して、同じテストを複数回実行することができます。

python
import pytest

@pytest.mark.parametrize("dividend, divisor, expected_quotient", [(10, 2, 5), (12, 3, 4)])
def test_division(dividend, divisor, expected_quotient):
    assert dividend / divisor == expected_quotient

この例では、@pytest.mark.parametrizeデコレータを使用して、3つの引数(dividenddivisorexpected_quotient)を指定し、それぞれの値をタプルで指定しています。テストは、それぞれのタプルの値を使用して、複数回実行されます。

fixture

pytestには、テストコードの再利用性を高めるために、fixtureという機能があります。fixtureは、テストコードの前処理や後処理を定義することができ、テストコード内で使用されるオブジェクトを提供することができます。

fixtureは、pytest.fixtureデコレータを使用して定義されます。以下は、単純なfixtureの例です。

python
import pytest

@pytest.fixture
def my_fixture():
    preprocess() # preprocess test code within this function
    yield obj # obj is the object used in the test code
    postprocess() # post-process the test code within this function

この例では、my_fixtureという名前のfixtureを定義しています。このfixtureは、yield文でobjというオブジェクトを返します。テストコードでは、my_fixtureを引数として受け取ることができます。

python
def test_my_test(my_fixture):
    # test using the object provided by my_fixture

この例では、test_my_testという名前のテストコードを定義しています。このテストコードは、my_fixtureというfixtureを引数として受け取っています。my_fixtureが提供するオブジェクトを使用してテストを行うことができます。

fixtureは、pytestがテストコードを実行する前に自動的に呼び出され、テストコードの前処理を行います。テストコードが完了した後、fixtureは自動的に後処理を行います。fixtureは、テストコード間でオブジェクトを共有することができ、コードの再利用性を高めることができます。

fixture のパラメータ化

fixtureには、パラメータを渡すこともできます。これにより、異なるパラメータを持つ複数のオブジェクトを返すことができます。

python
import pytest

@pytest.fixture(params=[1, 2, 3])
def my_fixture(request):
    return request.param

この例では、my_fixtureという名前のfixtureを定義しています。このfixtureは、paramsオプションを使用して、1, 2, 3という3つのパラメータを持つことができます。テストコードでは、my_fixtureを引数として受け取ることができます。このfixtureは、各パラメータごとにオブジェクトを返します。

python
def test_my_test(my_fixture):
    assert my_fixture > 0

この例では、test_my_testという名前のテストコードを定義しています。このテストコードは、my_fixtureというfixtureを引数として受け取っています。my_fixtureは、1, 2, 3という3つのパラメータに対してそれぞれオブジェクトを返すため、このテストコードは3回実行されます。

fixture の依存関係

fixtureは、他のfixtureに依存することができます。これにより、テストコードが必要とする複数のオブジェクトを一度に提供することができます。

python
import pytest

@pytest.fixture
def obj1():
    return ...

@pytest.fixture
def obj2(obj1):
    return ...

@pytest.fixture
def obj3(obj1, obj2):
    return ...

この例では、3つのfixtureを定義しています。obj1は独立したfixtureであり、obj2obj1に依存するfixtureです。また、obj3obj1obj2に依存するfixtureです。テストコードでは、obj3を引数として受け取ることができます。obj3は、obj1obj2が提供するオブジェクトを使用して生成されます。

python
def test_my_test(obj3):
    ...

この例では、test_my_testという名前のテストコードを定義しています。このテストコードは、obj3というfixtureを引数として受け取っています。obj3は、obj1obj2が提供するオブジェクトを使用して生成されます。

fixture のスコープ

fixtureは、テストセットの異なる部分で再利用することができます。fixtureには、次のスコープがあります。

スコープ 説明
function テストコードの実行前に実行され、その後削除される(デフォルト値)。
class 同じクラス内の全てのテストコードで再利用される。
module 同じモジュール内の全てのテストコードで再利用される。
session pytest が実行される間、全てのテストコードで再利用される。

スコープを指定するには、fixtureにscopeオプションを使用します。

python
import pytest

@pytest.fixture(scope="module")
def my_fixture():
    return ...

この例では、my_fixtureという名前のfixtureを定義しています。このfixtureは、scopeオプションに"module"を指定しています。つまり、このfixtureは同じモジュール内の全てのテストコードで再利用されます。

fixture のキャッシュ

fixtureは、同じテストコード内で複数回呼び出されることがあります。pytestは、同じfixtureを複数回実行する代わりに、最初の呼び出しの結果をキャッシュします。これにより、fixtureの実行時間を節約できます。

fixtureのキャッシュを無効にするには、autouseオプションを使用します。

python
import pytest

@pytest.fixture(autouse=True)
def my_fixture():
    return ...

この例では、my_fixtureという名前のfixtureを定義しています。このfixtureは、autouseオプションにTrueを指定しています。つまり、このfixtureは、テストコードに明示的に引数として渡す必要がなく、自動的に実行されます。また、このfixtureは、キャッシュされません。

fixture の自動検出

pytestは、デフォルトでconftest.pyという名前のファイルを自動的に検出します。conftest.pyに定義されたfixtureは、そのファイルを含むディレクトリ以下の全てのテストコードから参照することができます。これにより、fixtureの再利用性を高めることができます。

プラグイン

pytestには、様々なプラグインが用意されており、ユーザーが自由に利用することができます。プラグインは、pytestの機能を拡張したり、カスタマイズしたりするために使用されます。以下に、代表的なpytestのプラグインについて説明します。

  • pytest-cov
    pytest-covは、コードのカバレッジを測定するためのプラグインです。pytestでテストを実行する際に、カバレッジ情報を取得してくれます。HTML形式のレポートを出力することができるため、コードのカバレッジを視覚的に確認することができます。

  • pytest-html
    pytest-htmlは、テスト結果をHTML形式で出力するためのプラグインです。HTML形式で出力することで、テスト結果を視覚的に確認することができます。また、レポートにはスクリーンショットやログなどの情報も含めることができます。

  • pytest-xdist
    pytest-xdistは、複数のプロセスやマシンでテストを並列実行するためのプラグインです。これにより、テストの実行時間を短縮することができます。

  • pytest-flake8
    pytest-flake8は、コードの品質を検証するためのプラグインです。flake8という静的解析ツールを利用して、コードに問題がないかをチェックします。pytestでテストを実行する際に、flake8の結果も一緒に表示することができます。

  • pytest-django
    pytest-djangoは、Djangoフレームワークでのテストをサポートするためのプラグインです。Djangoのモデルやビューなどをpytestでテストすることができます。

  • pytest-selenium
    pytest-seleniumは、Selenium WebDriverを利用してWebアプリケーションのテストを実行するためのプラグインです。ブラウザを自動操作して、Webアプリケーションの挙動をテストすることができます。また、スクリーンショットを取得することもできます。

  • pytest-mock
    pytest-mockは、モックを利用してテストを実行するためのプラグインです。unittest.mockという標準ライブラリを利用して、モックオブジェクトを生成してテストすることができます。

プラグインの使い方

pytestのプラグインは、pipコマンドを使用してインストールすることができます。具体的には、以下のようなコマンドを実行します。

bash
$ pip install pytest-cov

これで、pytest-covプラグインがインストールされます。pytest-covプラグインを使用する場合は、pytestのコマンドライン引数として、--covオプションを指定します。--covオプションの後には、カバレッジを測定する対象のパッケージやモジュールを指定します。例えば、以下のようになります。

bash
$ pytest --cov=myapp tests/

これで、myappパッケージ以下のコードのカバレッジを測定することができます。カバレッジ情報は、ターミナルに出力されます。また、HTML形式のレポートを生成する場合は、--cov-reportオプションを指定します。例えば、以下のようになります。

bash
$ pytest --cov=myapp --cov-report=html tests/

これで、カバレッジ情報をHTML形式で出力することができます。

pytest-htmlプラグインを使用する場合も、同様にpipコマンドでインストールします。

bash
$ pip install pytest-html

テスト結果をHTML形式で出力するには、--htmlオプションを指定します。例えば、以下のようになります。

bash
$ pytest --html=report.html tests/

これで、report.htmlというファイルにテスト結果がHTML形式で出力されます。また、pytest-htmlプラグインは、--self-contained-htmlオプションを指定することで、HTMLファイルに必要なリソースを1つのファイルにまとめることもできます。例えば、以下のようになります。

bash
$ pytest --html=report.html --self-contained-html tests/

これで、report.htmlというファイルに必要なリソースが1つのファイルにまとめられて出力されます。

他のプラグインについても、インストール方法と使い方はそれぞれ異なりますが、pytestのコマンドライン引数にオプションを指定することで利用できるようになります。

テストのカバレッジの測定

pytest-covプラグインを使用することで、テストのカバレッジを測定することができます。以下は、pytest-covプラグインを使用したテストの例です。

bash
$ pytest --cov=my_module tests/

この例では、--covオプションを使用して、my_moduleという名前のモジュールのカバレッジを測定しています。pytest-covプラグインは、テストを実行し、モジュールのカバレッジを計算し、レポートを生成します。

テストのスキップ

テストが実行できない場合、スキップすることができます。例えば、以下のようにして、特定の条件でテストをスキップすることができます。

python
import pytest

def test_division():
    divisor = 0
    dividend = 10
    if divisor == 0:
        pytest.skip("Can't divide by zero")
    assert dividend / divisor == 5

この例では、divisor0の場合、pytest.skip()関数によってテストがスキップされます。

参考

https://docs.pytest.org/en/7.2.x/
https://github.com/pytest-dev/pytest

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!