介绍
与外部服务交互的测试代码可能很棘手。您需要确保您的代码正确处理所有可能的响应,并且它不会提出太多不必要的请求。但是,您如何可靠,有效地测试这种代码?
这就是vcr.py的来源。vcr.py背后的想法很简单:它记录了代码进行的HTTP交互,然后在测试运行时重新播放它们。这样,您的测试就可以模拟HTTP请求而不会实际使用外部服务。很整洁,对
在本文中,我们将仔细研究VCR.PY及其一些功能。我们将介绍如何使用vcr.py记录和重播HTTP交互,如何自定义其与匹配器和过滤器的行为以及如何使用VCR.PY使用的盒式文件来存储记录的交互。
入门
要开始使用vcr.py,您需要先安装它。您可以使用PIP进行此操作:
pip install vcrpy
安装了vcr.py后,您可以在测试中开始使用它。这是一个简单的例子:
import requests
import vcr
with vcr.use_cassette('test_api.yaml', record_mode='once'):
def test_api():
response = requests.get('https://api.example.com')
assert response.status_code == 200
在此示例中,我们使用vcr.py拦截了对https://api.example.com
的get请求。首次运行该测试时,VCR.PY将在名为test_api.yaml
的盒式文件中记录HTTP交互。在随后的运行中,vcr.py将从盒式文件中重播记录的交互,而不是提出新请求。
使用VCR录像带传递的Record_mode确定如何记录与外部API的相互作用。有四种可用的记录模式:
-
once
:此模式将在测试首次运行时将与外部API的交互记录。测试的后续运行将使用记录的交互,而不是向API提出新请求。 -
new_episodes
:此模式将记录与以前尚未记录的外部API的任何交互。如果已经记录了互动,将重播它而不是提出新请求。 -
all
:此模式将记录与外部API的所有交互,无论它们是否已被记录。这对于建立API的完整录音很有用。 -
none
:此模式将完全关闭记录,并且只会重播先前记录的交互。当您要确保您的测试仅使用记录的交互并不向外部API提出任何新请求时,此模式很有用。
在上一个示例中,record_mode
参数设置为'once'
,这意味着在第一次运行测试并随后在所有后续运行中重播时,与外部API的交互将记录下来。
使用盒式文件
盒式文件是vcr.py存储记录的HTTP交互的地方。默认情况下,盒式文件是YAML文件,但您也可以使用JSON或SQLITE。盒式文件可以由人类轻松读取和编辑,这使得可以轻松检查文件的内容并在必要时进行更改。
这是盒式文件的示例:
- request:
body: !!binary |
eyJ0ZXN0IjoidGVzdCJ9
headers:
Content-Type: application/json
method: POST
uri: https://api.example.com/foo
response:
body: !!binary |
{"id": "123", "foo": "bar"}
headers:
Content-Type: application/json
status:
code: 200
message: OK
在此示例中,我们有一个记录的单个交互。该交互由带有JSON请求主体向https://api.example.com/foo
的邮政请求组成,以及包含ID和FOO值的JSON主体的响应。
使用盒式文件时要记住的一件重要的事情是,如果您记录了很多互动,它可能会变得很大。为了帮助您,VCR.PY提供了多种选择,用于控制录制和存储在盒式文件中的相互作用。
。很重要
默认情况下,vcr.py匹配基于HTTP方法,URL和请求主体的HTTP交互。但是,如果您想在其他条件下匹配其他条件,例如标题或查询参数?
这就是匹配者的进来。匹配器允许您指定用于匹配HTTP交互的自定义标准。这是一个例子:
import vcr
def custom_matcher(request1, request2):
# Your custom matching logic here
return True
with vcr.use_cassette('my_test.yaml',
match_on=['method', 'uri', custom_matcher]):
def test_my_function():
# Code that makes HTTP requests
在此示例中,我们定义了一个自定义匹配器函数custom_matcher
,该函数根据我们的自定义标准匹配了两个请求,并返回True
。然后,我们在我们的use_cassette
Call中使用此自定义匹配器,以及HTTP方法和URI的默认匹配器。
过滤器
有时您可能需要在记录或播放后进行过滤或修改请求或响应。例如,您可能需要在响应中存储在盒式文件中之前从响应中过滤敏感数据。或者,您可能需要在发送请求之前修改请求标题。
vcr.py提供了两个用于过滤的选项:before_record
和before_playback
。这些选项允许您分别在录制或播放请求之前指定调用的功能。这是一个例子:
import vcr
def filter_request(request):
request.headers['Authorization'] = 'Bearer xxxxxxx'
return request
def filter_response(response):
response.headers.pop('Set-Cookie', None)
return response
with vcr.use_cassette('my_test.yaml',
before_record=filter_request,
before_playback=filter_response):
# Your test code here
在此示例中,我们定义了分别修改请求和响应的两个滤波器filter_request
和filter_response
。然后,我们分别将这些过滤器功能传递给before_record
和before_playback
use_cassette
的选项。
占位符
有时,您可能在响应中可能会有动态数据,而您不想在测试中进行硬编码。例如,如果您有一个创建新资源并返回唯一ID的测试,则可能需要使用占位符在录音带文件中表示该ID。
vcr.py支持盒式文件中的占位符。占位符是在播放期间用实际值替换的字符串。这是一个例子:
- request:
body: !!binary |
eyJ0ZXN0IjoidGVzdCJ9
headers:
Content-Type: application/json
method: POST
uri: https://api.example.com/foo
response:
body: !!binary |
{"id": "{{ ID }}", "foo": "bar"}
headers:
Content-Type: application/json
status:
code: 200
message: OK
在此示例中,我们使用响应主体中的{{ ID }}
占位符来表示API生成的唯一ID。当VCR.Py重新响应时,它将用录音过程中API返回的实际ID值代替占位符。
基本插图
假设我们有一个简单的烧瓶Web应用程序,该应用程序充当外部Web服务API。当我们向/hello
端点提出GET请求时,它会返回JSON响应:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/hello')
def hello():
return jsonify({'message': 'Hello, World!'})
if __name__ == '__main__':
app.run()
现在,让我们编写一个VCRPY测试以测试此API。我们将使用requests
库将HTTP请求发送到API:
import vcr
import requests
def test_hello_api():
with vcr.use_cassette('hello_api.yaml', record_mode='once'):
response = requests.get('http://localhost:5000/hello')
assert response.status_code == 200
assert response.json() == {'message': 'Hello, World!'}
在此测试中,我们使用use_cassette
上下文管理器将HTTP请求包装到我们的Blask应用程序中。我们要指定我们要使用盒式文件hello_api.yaml
来记录与API的交互,并且我们使用record_mode
参数来指定我们只想记录一次交互(即在测试的第一次运行中)。
首次运行此测试时,VCRPY将记录与烧瓶应用程序的交互,并将其保存到hello_api.yaml
文件中。在随后的测试运行中,VCRPY将从盒式文件中重播交互,而不是向烧瓶应用程序提出新的HTTP请求。
要运行此测试,我们可以简单地使用pytest等测试跑步者执行测试文件:
$ pytest test_hello_api.py
这将运行测试并输出断言的结果。如果测试通过,我们应该看到类似于以下内容的输出:
============================ test session starts ============================
collected 1 item
test_hello_api.py . [100%]
============================= 1 passed in 0.13s =============================
如果测试失败,我们将看到一条错误消息,指示哪个断言失败。
这是hello_api.yaml
盒式文件在VCRPY记录HTTP与Blask App(即我们的外部API)相互作用后的样子:
interactions:
- request:
body: null
headers:
Accept-Encoding: identity
Connection: keep-alive
Host: localhost:5000
User-Agent: python-requests/2.25.1
method: GET
uri: http://localhost:5000/hello
response:
body:
encoding: utf-8
string: '{"message": "Hello, World!"}'
headers:
Connection: Keep-Alive
Content-Length: '25'
Content-Type: application/json
Date: Mon, 05 Jul 2023 12:34:56 GMT
Keep-Alive: timeout=5, max=100
Server: Werkzeug/2.0.2 Python/3.9.5
status:
code: 200
message: OK
recorded_at: 2023-07-05T12:34:56.789012Z
recorded_with: vcrpy/4.1.1
此文件包含测试和烧瓶应用程序之间HTTP相互作用的YAML表示。 interactions
部分包含了所有记录的HTTP交互的列表,在这种情况下只有一个。 request
和response
部分分别包含请求和响应标题和身体。 recorded_at
和recorded_with
字段指出了何时及其进行录制的vcrpy。
结论
vcr.py是测试与外部服务交互的代码的强大工具。它记录和重播HTTP交互的能力使得编写可靠和高效的测试变得容易。使用vcr.py,您可以自定义匹配和过滤的交互方式,并使用盒式文件来检查和修改记录的交互。
我希望这篇文章能为您提供vcr.py及其功能的良好介绍。如果您有兴趣了解更多信息,我鼓励您检查official documentation并开始在自己的测试中尝试VCR.PY。