golang实现HTTP交互录制与回放的测试插件go-vcr的使用
Golang实现HTTP交互录制与回放的测试插件go-vcr的使用
go-vcr简介
go-vcr
通过记录HTTP交互并在未来运行中回放它们,简化测试过程,为代码提供快速、确定性和准确的测试。
安装
执行以下命令安装go-vcr
:
$ go get -v gopkg.in/dnaeon/go-vcr.v4
注意:如果从旧版本迁移,可能需要重新创建测试cassette。
基本使用
下面是一个使用go-vcr
的简单示例:
package helloworld_test
import (
"path/filepath"
"strings"
"testing"
"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
)
func TestHelloWorld(t *testing.T) {
// 创建recorder
r, err := recorder.New(filepath.Join("testdata", strings.ReplaceAll(t.Name(), "/", "_")))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
// 确保完成后停止recorder
if err := r.Stop(); err != nil {
t.Error(err)
}
})
client := r.GetDefaultClient()
url := "https://go.dev/VERSION?m=text"
resp, err := client.Get(url)
if err != nil {
t.Fatalf("Failed to get url %s: %s", url, err)
}
t.Logf("GET %s: %d\n", url, resp.StatusCode)
}
第一次运行此测试代码将创建testdata/TestHelloWorld.yaml
cassette,其中包含HTTP客户端与远程服务器之间的记录交互。
下次执行此测试时,go-vcr
将从cassette回放已记录的HTTP交互,而不是进行实际的外部调用。
自定义请求匹配
在回放模式中,可以通过定义recorder.MatcherFunc
函数自定义传入请求与记录的请求/响应对的匹配方式。
例如,以下匹配器将匹配方法、URL和正文:
func customMatcher(r *http.Request, i cassette.Request) bool {
if r.Body == nil || r.Body == http.NoBody {
return cassette.DefaultMatcher(r, i)
}
var reqBody []byte
var err error
reqBody, err = io.ReadAll(r.Body)
if err != nil {
log.Fatal("failed to read request body")
}
r.Body.Close()
r.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))
return r.Method == i.Method && r.URL.String() == i.URL && string(reqBody) == i.Body
}
...
// Recorder选项
opts := []recorder.Option{
recorder.WithMatcher(customMatcher),
}
rec, err := recorder.New("testdata/matchers", opts...)
if err != nil {
log.Fatal(err)
}
defer rec.Stop() // 确保完成后停止recorder
client := rec.GetDefaultClient()
resp, err := client.Get("https://www.google.com/")
...
Hooks
go-vcr
中的hooks是在回放不同阶段调用的常规函数,它们接收HTTP交互。
可以使用hooks在请求/响应保存到磁盘之前修改它们,或在返回给客户端之前修改它们,或者任何其他您可能想做的操作。
以下是移除所有请求中Authorization
头的hook示例:
// 移除所有请求中Authorization头的hook
hook := func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
return nil
}
// Recorder选项
opts := []recorder.Option{
recorder.WithHook(hook, recorder.AfterCaptureHook),
recorder.WithMatcher(cassette.NewDefaultMatcher(cassette.WithIgnoreAuthorization())),
}
r, err := recorder.New("testdata/filters", opts...)
if err != nil {
log.Fatal(err)
}
defer r.Stop() // 确保完成后停止recorder
...
透传请求
有时您希望允许特定请求直接传递到远程服务器而不记录任何内容。
以下是通过特定端点的请求的示例:
passthrough := func(req *http.Request) bool {
return req.URL.Path == "/login"
}
// Recorder选项
opts := []recorder.Option{
recorder.WithPassthrough(passthrough),
}
r, err := recorder.New("testdata/filters", opts...)
if err != nil {
log.Fatal(err)
}
defer r.Stop() // 确保完成后停止recorder
...
服务器端
VCR测试也可用于创建服务器端测试。使用recorder.HTTPMiddleware
与HTTP处理程序一起创建来自传入请求和处理程序响应的fixture。然后可以回放这些请求并与记录的响应进行比较以创建回归测试。
许可证
go-vcr
是开源的,采用BSD许可证授权。
更多关于golang实现HTTP交互录制与回放的测试插件go-vcr的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现HTTP交互录制与回放的测试插件go-vcr的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用go-vcr实现HTTP交互录制与回放测试
go-vcr是一个优秀的Golang测试库,它可以记录和回放HTTP交互,非常适合用于测试需要与外部API交互的代码。下面我将详细介绍go-vcr的使用方法和示例。
go-vcr的核心概念
go-vcr通过拦截HTTP请求,将第一次请求的真实响应记录下来,后续测试中直接使用记录的响应,从而实现:
- 测试不依赖网络连接
- 测试运行速度更快
- 测试结果可重现
- 避免向真实API发送过多请求
安装go-vcr
go get github.com/dnaeon/go-vcr/v3/recorder
基本使用示例
package main
import (
"fmt"
"io/ioutil"
"net/http"
"github.com/dnaeon/go-vcr/v3/recorder"
)
func main() {
// 启动一个记录器,模式为录制模式
r, err := recorder.New("fixtures/example")
if err != nil {
panic(err)
}
defer r.Stop() // 确保记录器停止并保存数据
// 创建一个使用记录器作为传输的HTTP客户端
client := &http.Client{
Transport: r, // 注入我们的记录器
}
// 发出HTTP请求
resp, err := client.Get("https://httpbin.org/get")
if err != nil {
panic(err)
}
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
测试中使用go-vcr
下面是一个完整的测试示例:
package main
import (
"net/http"
"testing"
"github.com/dnaeon/go-vcr/v3/recorder"
"github.com/stretchr/testify/assert"
)
func TestHTTPClient(t *testing.T) {
// 启动记录器,模式为录制模式
// 第一次运行会创建fixture文件,之后运行会使用它
r, err := recorder.New("fixtures/httpbin")
if err != nil {
t.Fatal(err)
}
defer r.Stop()
// 创建使用记录器的HTTP客户端
client := &http.Client{
Transport: r,
}
// 执行请求
resp, err := client.Get("https://httpbin.org/get")
if err != nil {
t.Fatalf("Failed to make request: %v", err)
}
defer resp.Body.Close()
// 验证响应
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
}
记录器模式
go-vcr有几种工作模式:
// 模式设置示例
r, err := recorder.New("fixtures/example")
if err != nil {
panic(err)
}
// 设置模式
r.SetMode(recorder.ModeRecordOnce) // 只录制一次,之后回放
// r.SetMode(recorder.ModeReplayOnly) // 只回放,不录制
// r.SetMode(recorder.ModePassthrough) // 直接传递请求,不录制
// r.SetMode(recorder.ModeDisabled) // 完全禁用
高级功能
1. 过滤敏感信息
r, err := recorder.New("fixtures/example")
if err != nil {
panic(err)
}
// 添加过滤器来移除敏感信息
r.AddFilter(func(i *cassette.Interaction) error {
delete(i.Request.Headers, "Authorization")
return nil
})
2. 自定义匹配器
默认情况下,go-vcr根据方法、URL和正文匹配请求。你可以自定义匹配逻辑:
r, err := recorder.New("fixtures/example")
if err != nil {
panic(err)
}
// 添加自定义匹配器
r.AddMatcher(func(r *http.Request, i cassette.Request) bool {
return r.URL.Path == i.URL
})
3. 禁用压缩
有些API返回压缩的响应,你可能想禁用压缩以便更容易检查fixture文件:
r, err := recorder.New("fixtures/example")
if err != nil {
panic(err)
}
r.DisableCompression = true
实际测试示例
func TestGitHubAPI(t *testing.T) {
// 设置记录器
r, err := recorder.New("fixtures/github")
if err != nil {
t.Fatal(err)
}
defer r.Stop()
// 设置模式为录制一次
r.SetMode(recorder.ModeRecordOnce)
// 创建客户端
client := github.NewClient(&http.Client{Transport: r})
// 调用API
repos, _, err := client.Repositories.List(context.Background(), "octocat", nil)
if err != nil {
t.Fatalf("Failed to get repositories: %v", err)
}
// 验证结果
if len(repos) == 0 {
t.Error("Expected some repositories, got none")
}
}
最佳实践
- 将fixture文件(.yml)加入版本控制
- 为不同的测试用例使用不同的fixture文件
- 定期更新fixture文件以确保它们不过时
- 在CI环境中使用
ModeReplayOnly
模式 - 过滤掉敏感信息如API密钥、认证令牌等
go-vcr是一个强大的工具,可以显著提高依赖外部HTTP服务的测试的可靠性和速度。通过合理使用,你可以创建既快速又可靠的测试套件。