golang轻松实现HTTP接口模拟测试的插件库gock的使用
Golang轻松实现HTTP接口模拟测试的插件库gock的使用
gock是一个功能强大的Go语言HTTP模拟测试库,它可以轻松地模拟任何基于net/http
标准库实现的HTTP请求。
特性
- 简单、表达力强的流式API
- 支持JSON/XML模拟的内置助手
- 支持持久性和临时TTL限制的模拟
- 完整的正则表达式HTTP请求匹配能力
- 支持通过方法、URL参数、headers和body匹配请求
- 可扩展和可插拔的HTTP匹配规则
- 能够在模拟和真实网络模式之间切换
- 兼容任何
net/http
兼容的客户端
安装
go get -u github.com/h2non/gock
基本使用示例
简单测试模拟
package test
import (
"io/ioutil"
"net/http"
"testing"
"github.com/nbio/st"
"github.com/h2non/gock"
)
func TestSimple(t *testing.T) {
defer gock.Off() // 测试执行后清除挂起的模拟
gock.New("http://server.com").
Get("/bar").
Reply(200).
JSON(map[string]string{"foo": "bar"})
res, err := http.Get("http://server.com/bar")
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 200)
body, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(body)[:13], `{"foo":"bar"}`)
// 验证没有挂起的模拟
st.Expect(t, gock.IsDone(), true)
}
请求头匹配
package test
import (
"io/ioutil"
"net/http"
"testing"
"github.com/nbio/st"
"github.com/h2non/gock"
)
func TestMatchHeaders(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
MatchHeader("Authorization", "^foo bar$").
MatchHeader("API", "1.[0-9]+").
HeaderPresent("Accept").
Reply(200).
BodyString("foo foo")
req, err := http.NewRequest("GET", "http://foo.com", nil)
req.Header.Set("Authorization", "foo bar")
req.Header.Set("API", "1.0")
req.Header.Set("Accept", "text/plain")
res, err := (&http.Client{}).Do(req)
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 200)
body, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(body), "foo foo")
// 验证没有挂起的模拟
st.Expect(t, gock.IsDone(), true)
}
请求参数匹配
package test
import (
"io/ioutil"
"net/http"
"testing"
"github.com/nbio/st"
"github.com/h2non/gock"
)
func TestMatchParams(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
MatchParam("page", "1").
MatchParam("per_page", "10").
Reply(200).
BodyString("foo foo")
req, err := http.NewRequest("GET", "http://foo.com?page=1&per_page=10", nil)
res, err := (&http.Client{}).Do(req)
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 200)
body, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(body), "foo foo")
// 验证没有挂起的模拟
st.Expect(t, gock.IsDone(), true)
}
JSON body匹配和响应
package test
import (
"bytes"
"io/ioutil"
"net/http"
"testing"
"github.com/nbio/st"
"github.com/h2non/gock"
)
func TestMockSimple(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
Post("/bar").
MatchType("json").
JSON(map[string]string{"foo": "bar"}).
Reply(201).
JSON(map[string]string{"bar": "foo"})
body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
res, err := http.Post("http://foo.com/bar", "application/json", body)
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 201)
resBody, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(resBody)[:13], `{"bar":"foo"}`)
// 验证没有挂起的模拟
st.Expect(t, gock.IsDone(), true)
}
模拟自定义http.Client和http.RoundTripper
package test
import (
"io/ioutil"
"net/http"
"testing"
"github.com/nbio/st"
"github.com/h2non/gock"
)
func TestClient(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
Reply(200).
BodyString("foo foo")
req, err := http.NewRequest("GET", "http://foo.com", nil)
client := &http.Client{Transport: &http.Transport{}}
gock.InterceptClient(client)
res, err := client.Do(req)
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 200)
body, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(body), "foo foo")
// 验证没有挂起的模拟
st.Expect(t, gock.IsDone(), true)
}
启用真实网络
package main
import (
"fmt"
"io/ioutil"
"net/http"
"github.com/h2non/gock"
)
func main() {
defer gock.Off()
defer gock.DisableNetworking()
gock.EnableNetworking()
gock.New("http://httpbin.org").
Get("/get").
Reply(201).
SetHeader("Server", "gock")
res, err := http.Get("http://httpbin.org/get")
if err != nil {
fmt.Errorf("Error: %s", err)
}
// 响应状态来自模拟
fmt.Printf("Status: %d\n", res.StatusCode)
// 服务器头也来自模拟
fmt.Printf("Server header: %s\n", res.Header.Get("Server"))
// 响应体是原始的
body, _ := ioutil.ReadAll(res.Body)
fmt.Printf("Body: %s", string(body))
}
调试拦截的HTTP请求
package main
import (
"bytes"
"net/http"
"github.com/h2non/gock"
)
func main() {
defer gock.Off()
gock.Observe(gock.DumpRequest)
gock.New("http://foo.com").
Post("/bar").
MatchType("json").
JSON(map[string]string{"foo": "bar"}).
Reply(200)
body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
http.Post("http://foo.com/bar", "application/json", body)
}
使用技巧
- 在测试开始时声明模拟
- 使用
defer gock.Off()
确保测试完成后清理模拟 - 对于并发代码,确保先声明模拟以避免竞态条件
- 先定义更复杂的模拟,再定义通用的
- 拦截自定义
http.Client
只需一次 - 拦截后恢复
http.Client
许可证
MIT - Tomas Aparicio
更多关于golang轻松实现HTTP接口模拟测试的插件库gock的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang轻松实现HTTP接口模拟测试的插件库gock的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用gock实现Golang HTTP接口模拟测试
gock是一个强大的Go语言HTTP模拟库,可以轻松拦截和模拟HTTP请求,非常适合单元测试和API开发。下面我将详细介绍gock的使用方法。
安装gock
go get -u gopkg.in/h2non/gock.v1
基本使用
1. 简单请求模拟
package main
import (
"fmt"
"net/http"
"gopkg.in/h2non/gock.v1"
)
func main() {
defer gock.Off() // 确保测试结束后关闭mock
// 设置mock
gock.New("http://api.example.com").
Get("/users").
Reply(200).
JSON(map[string]string{"name": "John"})
// 发起请求
res, err := http.Get("http://api.example.com/users")
if err != nil {
panic(err)
}
fmt.Println("Status:", res.Status)
// 处理响应...
}
2. 带参数的请求匹配
gock.New("http://api.example.com").
Post("/login").
MatchHeader("Content-Type", "^application/json$").
MatchParam("token", "^abc123$").
JSON(map[string]string{"username": "admin"}).
Reply(200).
JSON(map[string]string{"token": "xyz789"})
3. 动态响应
gock.New("http://api.example.com").
Get("/dynamic").
Reply(201).
SetHeader("Server", "gock").
JSON(func() map[string]string {
return map[string]string{
"id": "123",
"time": time.Now().String(),
}
})
测试示例
单元测试中使用gock
package main
import (
"net/http"
"testing"
"gopkg.in/h2non/gock.v1"
)
func TestGetUser(t *testing.T) {
defer gock.Off() // 测试完成后清理
// 设置mock
gock.New("http://api.example.com").
Get("/users/123").
Reply(200).
JSON(map[string]string{"id": "123", "name": "John"})
// 执行测试
res, err := http.Get("http://api.example.com/users/123")
if err != nil {
t.Fatalf("请求失败: %v", err)
}
if res.StatusCode != 200 {
t.Errorf("预期状态码200, 实际得到 %d", res.StatusCode)
}
// 验证所有mock是否被调用
if !gock.IsDone() {
t.Errorf("未完成所有预期的HTTP请求")
}
}
高级功能
1. 持久化mock
// 持久化mock,不会被Off()清除
gock.New("http://api.example.com").
Get("/persistent").
Persist().
Reply(200).
BodyString("persistent response")
2. 网络延迟模拟
gock.New("http://api.example.com").
Get("/slow").
Delay(2 * time.Second). // 2秒延迟
Reply(200)
3. 错误模拟
gock.New("http://api.example.com").
Get("/error").
ReplyError("connection refused")
4. 自定义匹配器
gock.New("http://api.example.com").
Post("/custom").
AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) {
return req.Header.Get("X-Custom") == "123", nil
}).
Reply(200)
最佳实践
- 及时清理:始终使用
defer gock.Off()
确保测试完成后清理mock - 验证调用:使用
gock.IsDone()
验证所有预期的请求都已被调用 - 组织mock:将相关的mock组织在一起提高可读性
- 避免过度mock:只mock必要的请求,保持测试的真实性
gock是Go语言中进行HTTP测试的强大工具,它简单易用但功能丰富,能够满足各种HTTP接口测试需求。通过合理使用gock,可以显著提高测试的可靠性和执行速度。