![[Python] pytestのmonkeypatchを理解する](https://images.ctfassets.net/ct0aopd36mqt/wp-thumbnail-a873a00568e5285b2051ff6c5aae2c3d/febef60aecfe576a837a7efcb96625eb/python_icatch.png)
[Python] pytestのmonkeypatchを理解する
こんにちは。サービス開発室の武田です。
Pythonでユニットテストを記述する際に、テスティングフレームワークとしてpytestを使用することが多いです。
さてユニットテストを実行する上で、一時的に環境を変更したいことがあります。具体的にはカレントディレクトリや環境変数を変更したいといったことなのです。そして大事なこととして、各テストの独立性は高い方がよいテストです。そのため変更した内容をテスト後、元に戻す必要があります。
pytestではそのためのしくみとしてmock
およびmonkeypatch
を提供しています。今回はmonkeypatch
について確認します。
monkeypatch
は次のメソッドを提供しています。
メソッド名 | 概要 |
---|---|
setattr | 対象の属性を設定する |
delattr | 対象の属性を削除する |
setitem | 辞書に値を設定する |
delitem | 辞書から値を削除する |
setenv | 環境変数を設定する |
delenv | 環境変数を削除する |
syspath_prepend | インポートリストの先頭にパスを追加する |
chdir | カレントディレクトリを変更する |
context | コンテキストマネージャーを返す |
使用頻度が高いのはsetitem
、setenv
、chdir
あたりではないでしょうか。さて、これらは次の例のようにsetupで使用することで、テスト共通の前処理を実現できます。
@pytest.fixture(autouse=True)
def setup_user(monkeypatch):
monkeypatch.setenv("USER", "devio")
なぜ自動的に元の状態に戻るのか
monkeypatch
を覚えておくと、余計なコードを書くことなく、スマートに一時的な設定を追加できます。ではなぜ「この書き方でいいのか」について、気になる人もいるでしょう。そういう時はソースコードをのぞいてみるといいですね。次のコードを見てみましょう(コメントは削除)。
@fixture
def monkeypatch() -> Generator[MonkeyPatch]:
mpatch = MonkeyPatch()
yield mpatch
mpatch.undo()
引数に指定していたmonkeypatch
の実体なわけですが、この中ではyield mpatch
をして呼び出し元に処理を返しつつ、その後でmpatch.undo()
をしています。つまりユーザーが記述するコードでは、単にmonkeypatch
の提供しているメソッドを呼んでいるだけなのですが、裏ではundo()
を呼び出すしくみが働いています。
このしくみがあるおかげで、ユーザーが自分で元に戻す処理を記述する必要がないわけです。スマートですね!
まとめ
pytestのmonkeypatch
は便利!使っていきましょう!