轻松录制和重播HTTP交互:VCR.PY指南
#教程 #python #测试 #http

介绍

与外部服务交互的测试代码可能很棘手。您需要确保您的代码正确处理所有可能的响应,并且它不会提出太多不必要的请求。但是,您如何可靠,有效地测试这种代码?

这就是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的相互作用。有四种可用的记录模式:

  1. once:此模式将在测试首次运行时将与外部API的交互记录。测试的后续运行将使用记录的交互,而不是向API提出新请求。

  2. new_episodes:此模式将记录与以前尚未记录的外部API的任何交互。如果已经记录了互动,将重播它而不是提出新请求。

  3. all:此模式将记录与外部API的所有交互,无论它们是否已被记录。这对于建立API的完整录音很有用。

  4. none:此模式将完全关闭记录,并且只会重播先前记录的交互。当您要确保您的测试仅使用记录的交互并不向外部API提出任何新请求时,此模式很有用。

在上一个示例中,record_mode参数设置为'once',这意味着在第一次运行测试并随后在所有后续运行中重播时,与外部API的交互将记录下来。

使用盒式文件

VCR Casstte

盒式文件是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_recordbefore_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_requestfilter_response。然后,我们分别将这些过滤器功能传递给before_recordbefore_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交互的列表,在这种情况下只有一个。 requestresponse部分分别包含请求和响应标题和身体。 recorded_atrecorded_with字段指出了何时及其进行录制的vcrpy。

结论

vcr.py是测试与外部服务交互的代码的强大工具。它记录和重播HTTP交互的能力使得编写可靠和高效的测试变得容易。使用vcr.py,您可以自定义匹配和过滤的交互方式,并使用盒式文件来检查和修改记录的交互。

我希望这篇文章能为您提供vcr.py及其功能的良好介绍。如果您有兴趣了解更多信息,我鼓励您检查official documentation并开始在自己的测试中尝试VCR.PY。