这是我参与[第三届青训营-后端场]笔记创作活动的第4篇笔记
网络测试
HTTP请求测试,使用Go标准库net/http/httptest进行测试。
以gin框架为例,先简单定义一个handle接口。
package test
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
type Request struct {
Name string `json:"name"`
}
func Hello(c *gin.Context) {
req := Request{}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusOK, gin.H{
"msg": "Bye Bye",
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": fmt.Sprintf("Hello %s", req.Name),
})
}
func setUpRouter() *gin.Engine {
r := gin.New()
r.Handle("POST", "/hello", Hello)
return r
}
使用httptest这个工具来mock一个http请求:httptest.NewRequest()和mock一个记录器记录响应:httptest.NewRecorder(),作为保存服务端响应的记录器。
package test
import (
"encoding/json"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestHTTP(t *testing.T) {
cases := []struct {
name string
param string
expect string
}{
{"success", `{"name":"haogee"}`, "Hello haogee"},
{"fail", "", "Bye Bye"},
}
r := setUpRouter()
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
req := httptest.NewRequest(
"POST",
"/hello",
strings.NewReader(c.param),
)
w := httptest.NewRecorder()
//发起请求,并将响应写入recorder中。
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
// 解析并检验响应内容是否符合预期
var resp map[string]string
err := json.Unmarshal([]byte(w.Body.String()), &resp)
assert.Nil(t, err)
assert.Equal(t, c.expect, resp["msg"])
})
}
}
执行单元测试的结果
$ go test -v -run TestHTTP
=== RUN TestHTTP
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST /hello --> demo/test.Hello (1 handlers)
=== RUN TestHTTP/success
2022/05/15 23:18:10 <nil>
=== RUN TestHTTP/fail
2022/05/15 23:18:10 <nil>
--- PASS: TestHTTP (0.03s)
--- PASS: TestHTTP/success (0.03s)
--- PASS: TestHTTP/fail (0.00s)
PASS
ok demo/test 0.561s
根据这个样例,定义一个测试的方案就是:使用httptest mock请求和recorder,执行服务,解析响应数据,再使用assert测试工具进行测试结果分析。
sql测试
- 对数据库的单元测试可用使用go-sqlmock实现 先进行mock初始化
var (
mock sqlmock.Sqlmock
err error
db *sql.DB
)
// TestMain是在当前package下,最先运行的一个函数,常用于初始化
func TestMain(m *testing.M) {
//把匹配器设置成相等匹配器,不设置默认使用正则匹配
db, mock, err = sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
if err != nil {
panic(err)
}
model.DB, err = gorm.Open(mysql.New(mysql.Config{
SkipInitializeWithVersion: true,
Conn: db,
}), &gorm.Config{})
if err != nil {
panic(err)
}
// m.Run 是调用包下面各个Test函数的入口
os.Exit(m.Run())
}
举个测试insert的例子
func TestInsert(t *testing.T) {
user := &model.User{
Name: "haogee",
Password: "123456",
}
user.CreatedAt = time.Now()
user.UpdatedAt = time.Now()
mock.ExpectBegin()
mock.ExpectExec("INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`password`) VALUES (?,?,?,?,?)").
WithArgs(user.CreatedAt, user.UpdatedAt, nil, user.Name, user.Password).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()
err := model.NewUserManagerRepository().Insert(user)
assert.Nil(t, err)
}