pytest で monkeypatch を利用する
pytest で monkeypatch する方法を確認します。
下記は公式サイトの紹介ページ。
How to monkeypatch/mock modules and environments
リファレンス。
環境
- Python 3.9.13
- pytest 7.1.2
Python と pytest 周りで mock する方法について
pytest の monkeypatch の話へはいる前に、自分の中での情報整理のため、Python と pytest 周りで mock に利用できるライブラリや機能について整理します。
名前 | 説明 |
---|---|
unittest.mock | Python の標準モジュール テストコードで unittest を利用する場合に利用 |
mock | Python の 3rd Party Library であるが、Python 3.3 以降は unittest.mock に取り込まれている |
monkeypatch | pytest の組み込み fixture |
pytest-mock | pytest の 3rd Party Plug-in mock の wrapper として機能する pytest の fixture を提供する、らしい |
サンプルコード
ディレクトリ構造
.
├── src
│ ├── __init__.py
│ └── sample.py
└── tests
├── __init__.py
└── test.py
テスト対象のコード
./src/sample.py
import os from typing import Optional class Sample: DEFAULT_CONFIG = {"version": "1.0"} def __init__(self, number: int): self.__number = number def get_version(self) -> str: return f"VERSION :{Sample.DEFAULT_CONFIG['version']}" def get_os_user(self) -> Optional[str]: return os.getenv("USER") def double(self) -> int: return self.__number * 2 def get_version_os_user(self) -> str: return f"VERSION :{Sample.DEFAULT_CONFIG['version']}, USER: {os.getenv('USER')}" # noqa: E501
テストコード
./tests/test.py
monkeypatch
という fixture を呼んだ関数内で、mock 用のメソッドを利用します。且つ monkeypatch
を利用している関数を fixture とすることもでき、テスト用の関数で利用できます。
import pytest from src.sample import Sample def test_get_version(monkeypatch): """辞書の上書き/削除 monkeypatch.setitem(mapping, name, value) monkeypatch.delitem(obj, name, raising=True) """ monkeypatch.setitem(Sample.DEFAULT_CONFIG, "version", "1.1") sample = Sample(2) assert sample.get_version() == "VERSION :1.1" def test_get_os_user(monkeypatch): """環境変数の上書き/削除 monkeypatch.setenv(name, value, prepend=None) monkeypatch.delenv(name, raising=True) """ monkeypatch.setenv("USER", "sui-chan") sample = Sample(2) assert sample.get_os_user() == "sui-chan" def test_double(monkeypatch): """メソッドの上書き/削除 monkeypatch.setattr(obj, name, value, raising=True) monkeypatch.delattr(obj, name, raising=True) """ monkeypatch.setattr(Sample, "double", lambda x: 10) sample = Sample(2) assert sample.double() == 10 @pytest.fixture def patch_version(monkeypatch): """monkeypatchを利用した関数を fixture として定義""" monkeypatch.setitem(Sample.DEFAULT_CONFIG, "version", "2.0") @pytest.fixture def patch_user(monkeypatch): """monkeypatchを利用した関数を fixture として定義""" monkeypatch.setenv("USER", "mikochi") def test_get_version_os_user(patch_version, patch_user): """上記で fixture とした関数を利用""" sample = Sample(2) assert sample.get_version_os_user() == "VERSION :2.0, USER: mikochi"
実行コマンド
$ pytest -v tests/test.py ============================== test session starts ============================== collected 4 items tests/test.py::test_get_version PASSED [ 25%] tests/test.py::test_get_os_user PASSED [ 50%] tests/test.py::test_double PASSED [ 75%] tests/test.py::test_get_version_os_user PASSED [100%] ============================== 4 passed in 0.02s ================================
その他
サンプルコード内で利用していないものでは、下記があります。