概要
Python でデコレータを使う方法を過去に紹介しました
デコレートされている関数を pytest でテストする場合にデコレータは必ず実行されてしまいます
デコレータを monkeypatch したいケースがあったのでコツや回避方法を紹介します
環境
- macOS 10.15.6
- Python 3.8.5
テストするコード
以下のようなデコレータを持つコードをテストすることを想定しています
def first_deco(msg):
def _first_deco(func):
def wrapper(*args, **kwargs):
message = generate_message(msg)
return func(*args, **kwargs) + msg
return wrapper
return _first_deco
@first_deco(msg="World")
def hello():
return "Hello"
デコレータ自体のパッチはできないっぽい
いきなり結論なのですが自分が試した感じだと直接デコレータ自体を monkeypatch することはできませんでした
例えば以下のように直接デコレータに対して monkeypatch をしてもパッチした処理はコールされませんでした
import pytest
import deco
from deco import hello
def test_first_deco(monkeypatch):
monkeypatch.setattr(deco, 'first_deco', lambda msg: "Python")
result = hello()
assert (result == "HelloPython")
また以下のようにパッチする関数をテスト側で定義してもダメでした
ただ個別で monkeypatch した関数をコールするとちゃんと monkeypatch は当たっている感じでしたが、なぜかデコレートした元の関数をコールするとパッチされた関数は呼ばれません
deco.first_deco(msg="Python")(hello)()
import pytest
import deco
from deco import hello
def dummy_deco(msg):
def _first_deco(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs) + "Python"
return wrapper
return _first_deco
def test_first_deco(monkeypatch):
monkeypatch.setattr(deco, 'first_deco', dummy_deco)
result = hello()
assert (result == "HelloPython")
というのを踏まえて対処方法を紹介します
デコレータの内部処理をパッチする
これが一番現実的かなと思います
デコレータの内部で読んでいる処理を外部の関数にしてその関数をパッチする感じです
調べてもこの方法が王道っぽいです
以下では generate_message
をデコレータの外に定義してその関数を monkeypatch することで対処しています
def generate_message(msg):
return msg
def first_deco(msg):
def _first_deco(func):
def wrapper(*args, **kwargs):
message = generate_message(msg)
return func(*args, **kwargs) + message
return wrapper
return _first_deco
@first_deco(msg="World")
def hello():
return "Hello"
テスト側では generate_message
をパッチします
import pytest
from deco import hello, generate_message
def test_first_deco(monkeypatch):
monkeypatch.setattr(deco, 'generate_message', lambda msg: "Python")
result = hello()
assert (result == "HelloPython")
undecorated を使う
そもそもデコレータが邪魔で処理自体が不要という場合は undecorated という便利なライブラリがあるのでこれを使うのも有効です
デコレータの処理は完全にスルーすることになるので注意が必要です
pipenv install undecorated
import pytest
from undecorated import undecorated
from deco import hello
def test_first_deco(monkeypatch):
org_hello = undecorated(hello)
result = org_hello()
assert (result == "Hello")
最後に
いろいろ調べましたが直接デコレータを monkeypatch することはできないっぽいです
対処として
- デコレータの内部処理を外部の関数として定義してそれをパッチする
- undecorated を使ってデコレータの処理自体をスルーさせる
の 2 パターンかなと思います
0 件のコメント:
コメントを投稿