概要
フィクスチャと組み込みフィクスチャ
pytestはPythonのテスティングフレームワークの1つであり、Python標準モジュールに組み込まれているunittestと並んでPythonのテスト実装でよく使われています。
テストコードを実装する際、テストケースによっては前処理(setup)や後処理(teardown)をする場合があります。pytestではフィクスチャという機能を用いて処理を実装しますが、その一部の機能として組み込みフィクスチャが用意されています。組み込みフィクスチャを利用することで前処理・後処理の実装にいろいろなメリットがあります。
- テストの再利用性が向上
- 組み込みフィクスチャは、一般的なテストシナリオに必要な処理を提供しているため、同じセットアップを繰り返し記述する必要がなくなります。たとえば、ファイルやディレクトリの操作には
tmp_path
を、標準入出力のキャプチャにはcapsys
が役立ちます。
- 組み込みフィクスチャは、一般的なテストシナリオに必要な処理を提供しているため、同じセットアップを繰り返し記述する必要がなくなります。たとえば、ファイルやディレクトリの操作には
- テストの可読性と簡潔化
- 組み込みフィクスチャはよく使われる操作の処理を包括しているため、コードが読みやすくなり、テストの目的が明確になります。たとえば、
monkeypatch
は一時的に関数やオブジェクトを置き換えることでテスト目的をより理解しやすくします。
- 組み込みフィクスチャはよく使われる操作の処理を包括しているため、コードが読みやすくなり、テストの目的が明確になります。たとえば、
- 柔軟な前処理と後処理
pytest
の組み込みフィクスチャは、必要なリソースのセットアップと解放を適切に処理します。たとえば、tmp_path
は一時ディレクトリを作成し、テストが終わった後に自動的に削除します。
- 高いメンテナンス性
- 組み込みフィクスチャを使用することで、テスト用コードの変更や修正が容易になります。複雑なテストセットアップが簡略化されるため、テストコードの変更時の修正量も減り、メンテナンスがしやすくなります。
本記事ではフィクスチャについては詳細に解説しませんが、下記の記事で説明をしています。フィクスチャについてご興味のある方や復習したい方はご参考ください。
組み込みフィクスチャの使い方
pytestには組み込みフィクスチャとして決められた予約語があり、それをテストコードの引数に記述することで機能を実現することが可能です。例えば下記の例ではcapsys
のコードを示しています。capsys
はテスト中の標準出力print()
や標準エラー出力をキャプチャし、テスト内で出力内容を確認できます。
# test_capsys_example.py
def test_capsys_example(capsys):
print("Hello, capsys!")
captured = capsys.readouterr() # 標準出力と標準エラー出力を取得
assert captured.out == "Hello, capsys!\n"
組み込みフィクスチャの一覧
pytestの組み込みフィクスチャを確認したい場合はこちらの公式サイトで確認してみてください。下図のようにさまざまな組み込みフィクスチャが用意されていることがわかります。
docs.pytest.org, https://docs.pytest.org/en/latest/reference/fixtures.html#reference-fixtures
よく使う組み込みフィクスチャの実装
前提
pytestの実行環境構築は下記の記事をご参考ください。
tmp_pathフィクスチャ
この組み込みフィクスチャはテストが実行される間だけ存在する一時ディレクトリを提供します。tmp_path
を使うと、簡単に一時ファイルやディレクトリを作成してテストでき、テスト終了後には自動的に削除されるため、テスト環境を汚さずに済みます。
なお3行目のfile
は、Path
オブジェクトです。これはPythonの標準ライブラリのpathlib
モジュールから提供されるクラスで、ファイルシステム上のパス(ファイルやディレクトリ)を表します。
def test_tmp_path(tmp_path):
# 一時ディレクトリ内にファイルを作成
file = tmp_path / "sample.txt"
file.write_text("Hello, tmp_path!") # 指定のパスに新しいファイルを作成しテキストを書き込む
# ファイルの存在と内容を確認
assert file.read_text() == "Hello, tmp_path!"
assert file.exists()
tmp_path_factoryフィクスチャ
この組み込みフィクスチャはtmp_path
と似ていますが、異なるテスト関数間で共有できる一時ディレクトリを作成するために使います。3行目のtmp_path_factory.mktemp()
でディレクトリ作成しtmp_path_factory
で作成したディレクトリはテストごとに一度だけ作成できるため、複数のテストで共通の一時ディレクトリを利用できます。
def test_tmp_path_factory(tmp_path_factory):
# 一時ディレクトリを作成(テスト間で共有可能)
shared_dir = tmp_path_factory.mktemp("shared")
file = shared_dir / "shared_sample.txt"
file.write_text("Hello, tmp_path_factory!")
# ファイルの存在と内容を確認
assert file.read_text() == "Hello, tmp_path_factory!"
assert file.exists()
capsysフィクスチャ
この組み込みフィクスチャはprint()
などで標準出力や標準エラーに出力される内容をキャプチャします。これを利用してcapsys.readouterr()
を呼び出すとテストの間に発生したすべての標準出力とエラー出力を取得できます。これにより、特定のメッセージが出力されているか確認でき、標準出力が正しいことを確認するテストに便利です。
def test_capsys(capsys):
print("Hello, capsys!") # 標準出力
print("Capturing output is useful") # 標準出力
# 出力のキャプチャと確認
captured = capsys.readouterr()
assert "Hello, capsys!" in captured.out
assert "Capturing output is useful" in captured.out
6行目のcaptured
にはcapsys.readouterr()
によってキャプチャされた標準出力(stdout)の内容や標準エラー(stderr)の内容が格納されています。captured
は以下のような2つの属性を持つオブジェクトです:
captured.err
: 標準エラーに出力された文字列(エラーメッセージなど)captured.out
: 標準出力に出力された文字列(print
文など)
上記のコードでは、2、3行目の標準出力にprint("Hello, capsys!")
とprint("Capturing output is useful")
があるので、captured.out
にはこれらの文字列が改行を含んだ形で格納されます(下記コードを参照)。このためassert "Hello, capsys!" in captured.out
やassert "Capturing output is useful" in captured.out
といったテストで、標準出力に期待する文字列が出力されているかを確認できます。
captured.out # "Hello, capsys!\nCapturing output is useful\n"
captured.err # "" (このコードでは標準エラーへの出力がないため空文字列)
monkeypatchフィクスチャ
この組み込みフィクスチャは環境変数の変更や既存関数の動作を一時的に上書きするための簡易的なモック機能を提供します。monkeypatch.setenv()
を使って環境変数を一時的に設定したり、monkeypatch.setattr()
を使って関数をモック化できます。これにより、テスト中にシステム環境や関数の振る舞いを自由に変更できます。
なおpytestで利用できるモックはたくさんあるため別の記事でまとめたいと思います。
import os
def test_monkeypatch(monkeypatch):
# 環境変数を一時的に設定
monkeypatch.setenv("MY_ENV_VAR", "test_value")
assert os.getenv("MY_ENV_VAR") == "test_value" # 環境変数が設定されているか確認
# 関数の動作を一時的に変更
def mock_function():
return "mocked!"
monkeypatch.setattr("os.getcwd", mock_function) # os.getcwdをモック化
assert os.getcwd() == "mocked!" # モック化された関数の結果を確認
まとめ
本記事では組み込みフィクスチャを解説しました。具体的に紹介した組み込みフィクスチャはよく利用されるものですが、実際にはたくさんの種類があります。まずは本記事で紹介した組み込みフィクスチャを覚えつつ、実際にテストコードを書いているときに適宜調べてみるとよいでしょう。