概要
pytest の monkeypatch という機能を使うとインスタンスメソッドや変数を簡単にモックすることができます
今回はいくつかある monkeypatch を使ったモックの作成方法を紹介します
環境
- macOS 10.15.5
- Python 3.8.3
インストール
pipenv install pytest
サンプルコード
テスト対象のコードを用意します
今回はわかりやすいように外部にアクセスするメソッドを作っています
vim user.py
from http.client import HTTPSConnection
class User():
FAVORITE_FRAMEWORKS = {
"ruby": "sinatra",
"swift": "spritekit",
"python": "flask"
}
def __init__(self, name, age):
self.name = name
self.age = age
def hello(self):
return("%s,%i" % (self.name, self.age))
def access(self):
con = HTTPSConnection("kaka-request-dumper.herokuapp.com")
con.request('GET', '/')
res = con.getresponse()
return res.read().decode()
テストコード
hello と access のテストをするコードを作成します
以下ではわざとエラーになるようにしてます
vim test_user.py
from user import User
def test_hello(monkeypatch):
u = User("hawksnowlog", 10)
assert(u.hello() == "hawksnowlog,5")
def test_access(monkeypatch):
u = User("hawksnowlog", 10)
assert(u.access() == '{"key":"value"}')
とりあえず実行
pipenv run pytest
どちらのテストもエラーになることが確認できると思います
メソッドをモックしてエラーにならないようにしてみる
今回はお試しなのでモックを使うことでテストがエラーにならないようにしてみます
monkeypatch.setattr
を使うことで User クラス内にある hello
と access
メソッドをモックし期待するレスポンスが返ってくるように修正します
vim test_user.py
from user import User
def test_hello(monkeypatch):
# hello メソッドにパッチを当ててレスポンスを操作する
# return は callable でなければならない
monkeypatch.setattr(User, "hello", lambda self: "hawksnowlog,5")
u = User("hawksnowlog", 10)
assert(u.hello() == "hawksnowlog,5")
def test_access(monkeypatch):
# モック用の内部メソッドを作成してそれを登録することもできる
def mock_return(self):
return '{"key":"value"}'
monkeypatch.setattr(User, "access", mock_return)
u = User("hawksnowlog", 10)
assert(u.access() == '{"key":"value"}')
setattr
は 3 つの引数を指定します
それぞれ「クラス」「メソッド名」「モック」になります
モックに関しては callable でなければなりません
モック用のメソッドを用意してそれを指定しても良いですし lambda 式が使える Python なのであれば lambda を指定しても OK です
モックするメソッドが引数を持つ場合は引数が同じになるようにしましょう
基本的にはこの setattr
が使えれば問題ないですが他にも monkeypatch することができます
辞書の値を変更する
例えば定数として持っている辞書の値を変更することができます
setitem
というパッチを当てます
def test_langs(monkeypatch):
monkeypatch.setitem(User.FAVORITE_FRAMEWORKS, "ruby", "rails")
assert(User.FAVORITE_FRAMEWORKS["ruby"] == "rails")
ちなみに辞書から要素を削除する delitem
というメソッドもあります
他には
delattr
で特定の関数や属性を削除することができますsetenv
は環境変数をテスト時に上書きすることができますdelenv
は環境変数をテスト時に削除することができますchdir
はテスト時の実行ディレクトリを変更することができますsyspath_prepend
はsys.path
で参照できる配列の先頭に指定の仮想パスを追加することができます
def test_syspath(monkeypatch):
monkeypatch.syspath_prepend("hoge")
assert(sys.path[0] == "hoge")
最後に
pytest の monkeypatch を試してみました
setattr
が基本なのでこれが使えれば大体はカバーできると思います
かなり強力なのでこれが使いたいためにテストフレームワークを pytest にする人もいるんじゃないかなと思います
0 件のコメント:
コメントを投稿