2023年1月25日水曜日

pytest でコンストラクタをモックする際の注意事項

pytest でコンストラクタをモックする際の注意事項

概要

monkeypatch.setattr を使う場合に少し注意が必要です

環境

  • macOS 11.7.2
  • python 3.10.2
  • pytest 7.2.0

ディレクトリ構成

tree -a test module 

test
├── __init__.py
└── test_user.py
module
├── __init__.py
├── profile.py
└── user.py

サンプルコード

User クラスが Profile クラスをカプセル化しています
この Profile クラスに monkeypatch 当てることを想定します

  • vim module/profile.py
class Profile():

    def __init__(self, lang):
        self.lang = lang
  • vim module/user.py
from module.profile import Profile


class User():

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.profile = Profile("ruby")

    def show(self):
        print(self.name)
        print(self.age)
        print(self.profile.lang)

テストコード

  • vim test/test_user.py
from module.user import User

def test_show():
    user = User("hawksnowlog", 10)
    user.show()

上記が通常のテストコードになります
これで実行すると Profile.lang は「ruby」が表示されます

このテストコードに Profile に対して monkeypatch を当ててみます

from module.user import User


class DummyProfile():

    def __init__(self, _):
        self.lang = "python"


def test_show(monkeypatch):
    # monkeypatch.setattr("module.profile.Profile", DummyProfile)  # これだとうまく動かない
    monkeypatch.setattr("module.user.Profile", DummyProfile)
    user = User("hawksnowlog", 10)
    user.show()

これで pipenv run pytest -s を実行すると Profile.lang が python になっているのが確認できると思います

ポイント

monkeypatch.setattr する際のモジュールですが本来であれば実際に存在するモジュールパスを指定したくなりますが setattr を引数 2 つで指定する場合は モジュールを使用する側のパスを指定 します

これが謎な仕様ではあるのですがこうすることでパッチを当てられます

引数 3 つで setattr する場合には存在するクラスやモジュールのパスを指定するのですが 2 つの場合だけ使用する側のパスを指定する必要があるようです

参考サイト

0 件のコメント:

コメントを投稿