[Python] pytestのmonkeypatchを理解する

[Python] pytestのmonkeypatchを理解する

Clock Icon2025.04.30

こんにちは。サービス開発室の武田です。

Pythonでユニットテストを記述する際に、テスティングフレームワークとしてpytestを使用することが多いです。

さてユニットテストを実行する上で、一時的に環境を変更したいことがあります。具体的にはカレントディレクトリや環境変数を変更したいといったことなのです。そして大事なこととして、各テストの独立性は高い方がよいテストです。そのため変更した内容をテスト後、元に戻す必要があります。

pytestではそのためのしくみとしてmockおよびmonkeypatchを提供しています。今回はmonkeypatchについて確認します。

monkeypatchは次のメソッドを提供しています。

メソッド名 概要
setattr 対象の属性を設定する
delattr 対象の属性を削除する
setitem 辞書に値を設定する
delitem 辞書から値を削除する
setenv 環境変数を設定する
delenv 環境変数を削除する
syspath_prepend インポートリストの先頭にパスを追加する
chdir カレントディレクトリを変更する
context コンテキストマネージャーを返す

使用頻度が高いのはsetitemsetenvchdirあたりではないでしょうか。さて、これらは次の例のように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は便利!使っていきましょう!

参考URL

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.