用pytest-httpserver来测试requests

在Python程序中,用requests发起网络请求,是常见的操作。 但如何测试,是一个麻烦的问题。 如果是单元测试,可以用pytest-mock;但如果是集成测试,用Stub的思路,则可以考虑pytest-httpserver

本文介绍如何使用pytest-httpserver,来对requests等涉及网络请求操作的代码,进行集成测试。

基本原理

利用pytest的fixture机制,为测试函数提供一个httpserver。 这是一个基于werkzeug启动(make_server)的真实服务,启动在当前环境中,host:port自动生成,默认不可见。

from werkzeug.serving import make_server

对这个Server的特定Request,设置特定的Response,以达到测试的目的。

简单示例

以下提供一个简单的代码样例,便于理解完整流程。

import requests
from pytest_httpserver import HTTPServer
from pytest_httpserver.httpserver import RequestHandler


def test_root(httpserver: HTTPServer):
    handler = httpserver.expect_request('/')
    assert isinstance(handler, RequestHandler)
    handler.respond_with_data('', status=200)

    response = requests.get(httpserver.url_for('/'))
    assert response.status_code == 200

httpserver需要设置两方面内容,输入(Request)和输出(Response)。 先通过expect_request指定输入,再通过respond_with_data指定输出。 最后,通过url_for来获取随机生成Server的完整URL。

这里,仅对/的Request响应,返回status=200的Response。

如果在使用一些不方便使用fixtures的场景,可以通过with来使用相同功能。

def test_root():
    with HTTPServer() as httpserver:
        handler = httpserver.expect_request('/')
        assert isinstance(handler, RequestHandler)
        handler.respond_with_data('', status=200)

        response = requests.get(httpserver.url_for('/'))
        assert response.status_code == 200

更多代码示例

代码示例,有时比API文档更管用。 (更何况这个库还没有文档。)

def test_status(httpserver: HTTPServer):
    uri = '/status'
    handler = httpserver.expect_request(uri)
    handler.respond_with_data('', status=302)

    response = requests.get(httpserver.url_for(uri))
    assert response.status_code == 302


def test_method(httpserver: HTTPServer):
    uri = '/method'
    handler = httpserver.expect_request(uri=uri, method='GET')
    handler.respond_with_data('', status=200)

    response = requests.get(httpserver.url_for(uri))
    assert response.status_code == 200
    response = requests.post(httpserver.url_for(uri))
    assert response.status_code == 500


def test_respond_with_data(httpserver: HTTPServer):
    uri = '/data'
    handler = httpserver.expect_request(
        uri=uri,
        method='POST',
    )
    handler.respond_with_data('good')

    response = requests.post(httpserver.url_for(uri))
    assert response.status_code == 200
    assert response.content == b'good'


def test_respond_with_json(httpserver: HTTPServer):
    uri = '/data'
    expect = {'a': 1, 'b': 2}
    handler = httpserver.expect_request(
        uri=uri,
        method='POST',
    )
    handler.respond_with_json(expect)
    handler.respond_with_data

    response = requests.post(httpserver.url_for(uri))
    assert response.status_code == 200
    assert expect == response.json()

以上的几个测试函数,展示了一些常用的手段:

其它

这个库当然还支持HTTP协议的其它内容,包括headersmimetypcontent_type等。 这些在测试中不常用,就不介绍了。


相关笔记