先日PythonのMockに悩まされまくったので備忘録を置いておきます。
とりあえず公式ドキュメント
[unittest.mock --- モックオブジェクトライブラリ]
(https://docs.python.org/ja/3/library/unittest.mock.html)
[unittest.mock --- 入門]
(https://docs.python.org/ja/3/library/unittest.mock-examples.html)
2時間ぐらい悩んだことが公式にさらっと書いてあった。
公式は偉大
requests.getをmockする
import requests
def get_qiita(url):
response = requests.get(url)
return response
urlでgetして返ってきたresponseを返すだけ
import unittest
from mock import Mock, patch
import tori01
class TestTori01(unittest.TestCase):
@patch("tori01.requests.get")
def test_get_response(self, requests_get):
requests_get.return_value.status_code = 200
res = tori01.get_response("mock.url")
self.assertEqual(res.status_code, 200)
ステータスコードでアサーションチェック
今回これらのコードを土台とする
patch周り
tori01.py
でrequests
がimportされているので
@patch("tori01.requests.get")
でpatchされる
これがtori01.py
でrequests.get
がimportされていると
@patch("tori01.get")
としないとpatchされない
# import requests から変更
from requests import get
def get_response(url):
response = get(url)
return response
import unittest
from mock import Mock, patch
import tori01
class TestTori01(unittest.TestCase):
# @patch("tori01.requests.get") から変更
@patch("tori01.get")
def test_get_response(self, requests_get):
requests_get.return_value.status_code = 200
res = tori01.get_response("mock.url")
self.assertEqual(res.status_code, 200)
`requests.get`ではなく、`requests`をmockしたい場合(どっちが正統なんだろうか)
import unittest
from mock import Mock, patch
import tori01
class TestTori01(unittest.TestCase):
# @patch("tori01.requests.get") から変更
# 引数の命名もrequests.get→requestsに変更
@patch("tori01.requests")
def test_get_response(self, requests):
# requests_get.return_value.status_code = 200 から変更
requests.get.return_value.status_code = 200
res = tori01.get_response("mock.url")
self.assertEqual(res.status_code, 200)
関数を呼び出す
呼び出された時の引数を確認することができる。
import unittest
from mock import Mock, patch
import tori01
class TestTori01(unittest.TestCase):
@patch("tori01.requests.get")
def test_get_response(self, requests_get):
# mockを返す関数を作り、呼び出されるようにする。
def requests_get_mock(url):
return Mock(status_code = 200, url = url)
requests_get.side_effect = requests_get_mock
res = tori01.get_response("mock.url")
self.assertEqual(res.status_code, 200)
self.assertEqual(res.url, "mock.url")
こんな回りくどいことをしなくても`assert_called_with`でチェックできる。 他のassert系も色々ある
import unittest
from mock import Mock, patch
import tori01
class TestTori01(unittest.TestCase):
@patch("tori01.requests.get")
def test_get_response(self, requests_get):
requests_get.return_value.status_code = 200
res = tori01.get_response("mock.url")
self.assertEqual(res.status_code, 200)
requests_get.assert_called_with("mock.url") #追加
引数によって戻り値を変化
import unittest
from mock import Mock, patch
import tori01
class TestTori01(unittest.TestCase):
@patch("tori01.requests.get")
def test_get_response(self, requests_get):
vals = {"mock.url":200, "error_mock.url":404}
def requests_get_mock(val):
return Mock(status_code = vals[val])
requests_get.side_effect = requests_get_mock
res = tori01.get_response("mock.url")
res_error = tori01.get_response("error_mock.url")
self.assertEqual(res.status_code, 200)
self.assertEqual(res_error.status_code, 404)
例外を投げることもできる
import unittest
import requests
from mock import Mock, patch
import tori01
class TestTori01(unittest.TestCase):
@patch("tori01.requests.get")
def test_get_response(self, requests_get):
# side_effectに例外クラスを設定すれば例外が投げられる
requests_get.side_effect = requests.HTTPError
with self.assertRaises(requests.HTTPError):
tori01.get_response("mock.url")
追記したり、その2を書くかも
MagicMockとか…