pytestでhttp clientのテストを実装する際に、
pytest-httpserverというライブラリが便利だったので、
使い方のメモを残しておきます。

pytest-httpserver | GitHub
https://github.com/csernazs/pytest-httpserver

このライブラリを使うと、pytestのfixtureとして、
テスト用のhttp serverを実行する事ができます。
httpを利用して他システムと連携する機能などをテストする場合に便利です。

このエントリでは、いくつかのテスト例を示しますので、
公式のサンプルをあわせて参考にして頂ければと思います。

Howto | pytest_httpserver docs
https://pytest-httpserver.readthedocs.io/en/latest/howto.html

単純なテスト

次のコードが、ドキュメントの冒頭に記載されているサンプルです(少し変更してます)。

test_basic.py

import requests
from pytest_httpserver.httpserver import HTTPServer
def test_json_client(httpserver: HTTPServer):
    httpserver.expect_request("/foobar").respond_with_json({"foo": "bar"})
    assert requests.get(httpserver.url_for("/foobar")).json() == {"foo": "bar"}

次のように依存ライブラリをインストールして実行します。

pip install pytest-httpserver
pip install requests
python -m pytest test_basic.py

簡単に説明すると、
test_json_clientメソッドの引数httpserverがhttp serverのfixtureで、

次のコードで、予測されるHTTPのリクエスト・レスポンスを設定 httpserver.expect_request("/foobar").respond_with_json({"foo": "bar"})

次のコードで、HTTPのリクエストを実行
assert requests.get(httpserver.url_for("/foobar")).json() == {"foo": "bar"}

という流れになります。

ファイルを返却するテスト

httpserverからファイルを返却したい場合は、
次のように、ファイルをreadしてresponse_dataに設定することで実現できます。

test_file.py

import requests
from pytest_httpserver.httpserver import HTTPServer
def test_response_file(httpserver: HTTPServer):
    body = open("./sample.png", "rb").read()
    httpserver.expect_request("/foobar").respond_with_data(
        content_type="image/png",
        response_data=body,
    )
    resp = requests.get(httpserver.url_for("/foobar"))
    assert int(resp.headers["Content-Length"]) == len(body)
    assert resp.content == body

リクエストが呼ばれたかどうかのテスト

呼び出したリクエスト・返却したレスポンスは、httpserver.logに記録されているので、
この内容をチェックすればチェックができます。
ここではPyHamcrestを使った実装の例を示します。
# unittest.mockassert_has_callsみたいなものがあればよいのですが、

PyHamcrest
https://github.com/hamcrest/PyHamcrest

test_assert_request.py

import requests
from hamcrest import assert_that, has_item, has_entries
from pytest_httpserver.httpserver import HTTPServer
def test_assert_request(httpserver: HTTPServer):
    httpserver.expect_request("/foobar").respond_with_json({"foo": "bar"})
    assert requests.get(httpserver.url_for("/foobar?x=1")).json() == {"foo": "bar"}
    assert_that(
        [{"_path":req.path, **dict(req.args)} for (req, res) in httpserver.log],
        has_item(has_entries({"x": "1", "_path": "/foobar"}))
    )

PyHamcrestは、次のようにインストールします。

pip install PyHamcrest

HTTPS(SSL)のリクエストのテスト

HTTPS(SSL)のテスト方法は、次のドキュメントに書かれています。

Running an HTTPS server | pytest_httpserver docs
https://pytest-httpserver.readthedocs.io/en/latest/howto.html#running-an-https-server

ここでは、上記のリンクとは少し変更して、
環境変数REQUESTS_CA_BUNDLEに証明書を設定する例を示します。

test_https.py

import pytest
import trustme
import requests
import os
import ssl
from pytest_httpserver.httpserver import HTTPServer

@pytest.fixture(scope="session")
def ca():
    return trustme.CA()

@pytest.fixture(scope="session")
def httpserver_ssl_context(ca):
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    localhost_cert = ca.issue_cert("localhost")
    localhost_cert.configure_cert(context)
    return context

@pytest.fixture(scope="session")
def httpclient(ca):
    with ca.cert_pem.tempfile() as ca_temp_path:
        os.environ["REQUESTS_CA_BUNDLE"] = ca_temp_path
        yield ca_temp_path

def test_requests(httpserver: HTTPServer, httpclient):
    httpserver.expect_request("/").respond_with_data("hello world!")
    result = requests.get(httpserver.url_for("/"))
    assert result.text == "hello world!"

trustmeは、次のようにインストールします。

pip install trustme

以上。