本文主要针对单元测试工具,其他工具请看专栏内其它博客。
httptest
**介绍:**Go 内置标准库net/http/httptest,核心用途用于测试net/http构建的HTTP服务(如API接口、Web服务等),它可以模拟HTTP请求发送和HTTP响应的接收,无需启动真实的HTTP服务器即可完成接口测试,极大提升了测试的便捷性和执行效率
优点:
- 无需启动真实服务器:无需调用
http.ListenAndServe启动端口监听,直接测试 HTTP 处理器(Handler/HandlerFunc),测试执行更高效。 - 脱离网络依赖:模拟 HTTP 请求与响应的完整生命周期,不受网络波动、端口占用等外部因素影响,测试结果稳定可复现。
- 精准捕获响应细节:可完整获取响应状态码、响应头、响应体等所有信息,便于精准断言验证。
- 支持两种核心测试场景:
- 测试 HTTP 处理器(直接调用
ServeHTTP,最常用) - 启动临时测试服务器(模拟真实服务端,用于客户端测试或集成测试)
- 测试 HTTP 处理器(直接调用
1.安装
内置工具可以直接使用
2.使用示例
相关代码在gitee代码仓库的示例代码中,仓库地址请看博客开头
blog.go
go
package httptest_demo
import (
"errors"
"fmt"
"io"
"net/http"
)
func SearchHttp(targetURL string) (interface{}, error) {
resp, err := http.Get(targetURL)
if err != nil {
errMsg := fmt.Sprintf("发送 GET 请求失败:%v", err)
fmt.Println(errMsg)
return nil, errors.New(errMsg)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
errMsg := fmt.Sprintf("请求失败,状态码:%d,状态信息:%s", resp.StatusCode, resp.Status)
fmt.Println(errMsg)
return nil, errors.New(errMsg)
}
bodyBytes, _ := io.ReadAll(resp.Body)
return string(bodyBytes), nil
}
blog_test.go
go
package httptest_demo
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestSearchHttp(t *testing.T) {
// --------------- 1. 定义所有测试场景的表驱动用例(循环遍历执行) ---------------
testCases := []struct {
name string // 用例名称
prepareFunc func() string // 前置准备:创建模拟服务器/构造URL,返回待请求的URL
expectedErr bool // 是否预期返回错误
errContains string // 预期错误信息包含的关键字(非空则验证)
expectedNonEmpty bool // 正常场景下,是否预期返回非空字符串
}{
// 场景1:正常请求(200 OK,响应体正常)
{
name: "正常请求-状态码200",
prepareFunc: func() string {
// 启动模拟HTTP服务器,返回200和测试响应体
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mockBody := "<!DOCTYPE html><html><title>百度一下</title></html>"
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(mockBody))
}))
// 关键:将mockServer放入测试上下文,确保后续能关闭(避免资源泄露)
t.Cleanup(func() { mockServer.Close() })
return mockServer.URL
},
expectedErr: false,
errContains: "",
expectedNonEmpty: true,
},
// 场景2:请求失败(无效URL,模拟网络异常)
{
name: "异常场景-无效URL请求失败",
prepareFunc: func() string {
// 返回一个无效的URL,触发http.Get请求失败
return "http://invalid-xxx-url-12345/"
},
expectedErr: true,
errContains: "发送 GET 请求失败",
expectedNonEmpty: false,
},
// 场景3:状态码非200(模拟404 Not Found)
{
name: "异常场景-状态码404",
prepareFunc: func() string {
// 启动模拟HTTP服务器,返回404状态码
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte("页面不存在"))
}))
t.Cleanup(func() { mockServer.Close() })
return mockServer.URL
},
expectedErr: true,
errContains: "请求失败,状态码:404",
expectedNonEmpty: false,
},
// 场景4:状态码非200(模拟500服务器内部错误)
{
name: "异常场景-状态码500",
prepareFunc: func() string {
// 启动模拟HTTP服务器,返回500状态码
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("服务器内部错误"))
}))
t.Cleanup(func() { mockServer.Close() })
return mockServer.URL
},
expectedErr: true,
errContains: "请求失败,状态码:500",
expectedNonEmpty: false,
},
}
// --------------- 2. 循环遍历所有测试用例,统一执行验证 ---------------
for _, tc := range testCases {
// 循环内使用t.Run创建子用例(便于精准定位失败场景,不影响其他用例)
t.Run(tc.name, func(t *testing.T) {
// 步骤1:执行前置准备,获取待请求的URL
targetURL := tc.prepareFunc()
// 步骤2:调用被测函数
result, err := SearchHttp(targetURL)
// 步骤3:统一断言验证
// 3.1 验证错误是否符合预期
if (err != nil) != tc.expectedErr {
t.Fatalf("错误预期不符:预期是否错误[%t],实际是否错误[%t],错误信息[%v]",
tc.expectedErr, err != nil, err)
}
// 3.2 若预期错误,验证错误信息是否包含指定关键字
if tc.expectedErr && tc.errContains != "" {
if !strings.Contains(err.Error(), tc.errContains) {
t.Errorf("错误信息不符:预期包含[%s],实际错误[%v]", tc.errContains, err)
}
}
// 3.3 验证返回值是否符合预期
if tc.expectedErr {
// 异常场景:预期返回nil
if result != nil {
t.Errorf("异常场景预期返回nil,实际返回[%v],类型[%T]", result, result)
}
} else {
// 正常场景:验证返回值是string类型,且非空(若预期非空)
resultStr, ok := result.(string)
if !ok {
t.Fatalf("正常场景预期返回string类型,实际返回[%T]", result)
}
if tc.expectedNonEmpty && len(resultStr) == 0 {
t.Error("正常场景预期返回非空字符串,实际返回空字符串")
}
}
})
}
}
命令行执行命令
go test -cover
结果:
powershell
PS D:\wyl\workspace\go\tracer\logic\httptest_demo> go test -cover
发送 GET 请求失败:Get "http://invalid-xxx-url-12345/": dial tcp: lookup invalid-xxx-url-12345: no such host
请求失败,状态码:404,状态信息:404 Not Found
请求失败,状态码:500,状态信息:500 Internal Server Error
PASS
coverage: 100.0% of statements
ok tracer/logic/httptest_demo 1.683s