VCR.py:给 Python HTTP 测试按下录制键

写测试遇到发 HTTP 请求的代码,多少人都卡在这一步。每次跑测试都要真的发请求出去,网络慢了等网络,接口改了挂测试,想离线干活直接没法跑。

VCR.py 的思路很直接。把第一次跑测试时的 HTTP 交互全部录下来,存成一个文件,叫 cassette。之后再跑同样的测试,它就不真的发请求了,直接放录好的响应回来。

这个项目是 Kevin McCarthy 用 Python 实现的,灵感来自 Ruby 社区的 VCR 库。目前在 GitHub 上拿到了 2968 Star。

正文顶部截图

1、 这东西怎么用

用法就是用一个装饰器或者上下文管理器把测试包起来:

import vcr
import urllib.request

with vcr.use_cassette('fixtures/vcr_cassettes/synopsis.yaml'):
    response = urllib.request.urlopen('https://httpbin.org/').read()

第一次跑的时候,VCR.py 会监听所有 HTTP 请求,真正发出去,然后把请求和响应一起序列化存到 synopsis.yaml 文件。第二次再跑同样的测试,它直接从文件里读出响应,不碰网络。

用装饰器也一样:

@vcr.use_cassette('fixtures/vcr_cassettes/synopsis.yaml')
def test_my_code():
    response = urllib.request.urlopen('https://httpbin.org/').read()
    assert response

录下来的 cassette 文件内容大致是这样的结构:记录了请求的 method、path、headers,以及对应的响应状态码、响应头和响应体。可读性不错,可以直接打开看里面有什么,也能手动修改。

VCR.py 支持多种序列化格式。默认用 yaml,也可以换成 json。如果项目有特殊需求,还能写自定义的序列化器接进去。## 2、 这么做的好处是什么

第一,测试速度上来了。不用等网络往返,本地读文件几乎是瞬间的事。

第二,测试变得确定。接口会不会挂、网络会不会超时,这些外部因素不再影响测试结果。今天能过明天也能过,不会出现刚才还绿着突然就红了的情况。

第三,可以离线跑。火车上、飞机上,没网也一样跑测试。

还有一个很实用的场景:如果上游 API 改了,只需要删掉 cassette 文件重新跑一次测试,VCR.py 会自动重新录制,更新所有录好的数据。

Cassette 文件默认存成 yaml 格式,也可以换成 json。录制时支持配置匹配策略,默认按请求方法、URL 和请求体来匹配,也可以改成只匹配方法加路径,或者完全按顺序匹配。灵活度够用。

README区域截图

3、 配合 Pytest

VCR.py 社区有一个配套库叫 pytest-recording,提供了 pytest fixture 风格的用法,用起来更顺手:

def test_my_code(cassette):
    response = urllib.request.urlopen('https://httpbin.org/').read()
    assert response

在 conftest.py 里配置好 cassette 路径,每个测试自动加载对应的录制数据。

4、 哪些人在用

VCR.py 适合所有需要跟外部 HTTP 服务打交道的 Python 项目。写 SDK 的、接第三方 API 的、做微服务调用的,都能用上。只要测试里会发 HTTP 请求,它就能帮你管起来。

不挑 HTTP 库。requests、urllib、httpx、aiohttp 都支持。不管同步异步,同一个 cassette 格式统一管理。

项目本身也在持续维护。支持 Python 3.8 到 3.13,对主流版本覆盖得很全。社区活跃度一直不错,issue 和 PR 都有回应。

5、 注意事项

Cassette 文件默认存成 yaml 格式,也可以用 json。录下来的数据里如果含敏感信息,比如 token 或密码,VCR.py 提供了 filter 机制,在录制时自动替换掉,避免把密钥提交到代码仓库。

@vcr.use_cassette('fixtures/cassette.yaml',
                   filter_headers=['authorization'])
def test_with_sensitive_data():
    pass

6、 总结

VCR.py 解决的是一个具体且普遍的问题:HTTP 测试的依赖和速度。方案也很直接:录一次,放无数遍。不引入复杂的 mock 框架,不要求改代码结构,加个装饰器就行。

对于维护 Python HTTP 客户端的团队来说,这个库能省下不少维护测试的时间。

次,放无数遍。不引入复杂的 mock 框架,不要求改代码结构,加个装饰器就行。

对于维护 Python HTTP 客户端的团队来说,这个库能省下不少维护测试的时间。

更多推荐