HTTPリクエストをモックするには
PythonでHTTPリクエストをモックするには、どうしたらいいか。unittest.mockを使ってHTTPリクエストを投げるメソッドをモックする方法が思い付くかもしれない。しかし、どこをモックして何を返すようにしたらよいのかは自明ではない。
HTTPrettyというライブラリを使うと、簡単にHTTPリクエストをモックできる。どんなライブラリを使っているのか、インタフェースがどうなのかを気にする必要がない。
テスト中は常にモックする
PerlのTest::WWW::Stubに近いことをPythonでも実現したい。つまり、テスト中は常にHTTPリクエストをモックして外部にリクエストが飛ばないようにしたい。
pytestを使っているなら、以下のようなfixtureをconftest.pyに書くのが手軽だと思う。
import httpretty import pytest @pytest.fixture(scope="function", autouse=True) def mock_http_request(): with httpretty.enabled(allow_net_connect=False): yield
こうすると、外部にHTTPリクエストを飛ばしたら例外が送出されてテストが落ちるようになる。
Python標準のunittestでは試してないけど、TestCaseクラスを継承して、HTTPリクエストをモックするsetUpメソッド (片付けるtearDownメソッド) を実装すれば実現できるのではないか。
特定のリクエストに対してダミーレスポンスを返す
特定のURLへのリクエストにダミーのレスポンスを返したいなら、 yield
の前で httpretty.register_uri
関数を呼び出すようにしたらいいし、テストメソッド内で呼ぶようにしてもよさそう。ダミーレスポンスを適当にファイルに切り出しておいたら取り回しがいいと思う。
以下の例では、Slack APIの呼び出しをモックしてダミーのレスポンスを返すように差し替えている。
from pathlib import Path import httpretty import pytest @pytest.fixture(scope="function", autouse=True) def mock_http_request(): with httpretty.enabled(allow_net_connect=False): body = ( Path(__file__).parent / "data/httpmock/slack_chat_postMessage.json" ).read_text() httpretty.register_uri( httpretty.POST, re.compile(r"^https://(www[.])?slack[.]com/api/chat[.]postMessage$"), body=body ) yield
他の方法との比較
テストメソッドごとにHTTPリクエストをモックするかどうか決めたいならpytest-httprettyというプラグインが便利そう。httpretty
というマーカーをテストに設定したらHTTPリクエストがモックされるようになる。
個人的には、常にHTTPリクエストがモックされている方が、テストの再現性や本番のAPIを叩いてしまうことを回避する側面などを考えると、よりよいと思ったので自分で仕組みを用意した。モックしたいリクエストだけモックするのではなく、常に外部リクエストが飛ばないようにして必要に応じて開放する方が安心できると思う。