go自带的测试包 #
简单测试示例 #
-
创建测试文件xxx_test.go
-
创建测试函数TestXXX(t *testing.T)
func TestUser(t *testing.T) {
fmt.Println("开始测试用户")
t.Run("测试子函数",addUser)
}
func addUser(t *testing.T) {
t.Log("我的第一函数")
}
#go test -v --run TestUser xxx_test.go[文件名可以不用指定]
#output
=== RUN TestUser
开始测试用户
=== RUN TestUser/测试子函数
main_test.go:19: 我的第一函数
--- PASS: TestUser (0.00s)
--- PASS: TestUser/测试子函数 (0.00s)
PASS
ok test 2.212s
testing.M测试函数执行之前做一些其他操作
func TestMain(m *testing.M) {
fmt.Println("所有测试执行执行")
m.Run()
}
#go test -v
#output
所有测试执行执行
=== RUN TestUser
开始测试用户
=== RUN TestUser/测试子函数
main_test.go:19: 我的第一函数
--- PASS: TestUser (0.00s)
--- PASS: TestUser/测试子函数 (0.00s)
PASS
ok test 2.009s
setup 和 teardown #
如果在同一个测试文件中,每一个测试用例运行前后的逻辑是相同的,一般会写在 setup 和 teardown 函数中。例如执行前需要实例化待测试的对象,如果这个对象比较复杂,很适合将这一部分逻辑提取出来;执行后,可能会做一些资源回收类的工作,例如关闭网络连接,释放文件等。标准库 testing
提供了这样的机制:
func setup() {
fmt.Println("Before all tests")
}
func teardown() {
fmt.Println("After all tests")
}
func Test1(t *testing.T) {
fmt.Println("I'm test1")
}
func Test2(t *testing.T) {
fmt.Println("I'm test2")
}
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}
testing.t的函数 #
t.FailNow() //立即终止测试
t.Fail() //仅标记不终止
单元测试框架提供的日志方法
方 法 备 注
Log 打印日志,同时结束测试
Logf 格式化打印日志,同时结束测试
Error 打印错误日志,同时结束测试
Errorf 格式化打印错误日志,同时结束测试
Fatal 打印致命日志,同时结束测试
Fatalf 格式化打印致命日志,同时结束测试
基准测试——获得代码内存占用和运行效率的性能数据 #
package tests
import "testing"
func Benchmark_Add(b *testing.B) {
var n int
for i := 0; i < b.N; i++ {
n++
}
}
# go test -v -bench=Benchmark_Add benchmark_census_test.go
-bench可以是.可以是Add也是可以是Benchmark_Add
#output
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz
Benchmark_Add
Benchmark_Add-4 1000000000 0.4148 ns/op
PASS
ok command-line-arguments 2.569s
基准测试框架对一个测试用例的默认测试时间是 1 秒。开始测试时,当以 Benchmark 开头的基准测试用例函数返回时还不到 1 秒,那么 testing.B 中的 N 值将按 1、2、5、10、20、50……递增,同时以递增后的值重新调用基准测试用例函数
通过
-benchtime
参数可以自定义测试时间
go test -v -bench=. -benchtime=5s benchmark_census_test.go
goos: linux
goarch: amd64
Benchmark_Add-4 10000000000 0.33 ns/op
PASS
ok command-line-arguments 3.380s
在命令行中添加
-benchmem
参数以显示内存分配情况
go test -v -bench=Add -benchmem benchmark_census_test.go
go test -v -bench=. -benchmem benchmark_census_test.go
go test -v -bench=Benchmark_Add -benchmem benchmark_census_test.go
#output
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz
Benchmark_Add
Benchmark_Add-4 1000000000 0.4203 ns/op 0 B/op 0 allocs/op
PASS
ok command-line-arguments 2.665s
控制计时器 #
有些测试需要一定的启动和初始化时间,如果从 Benchmark() 函数开始计时会很大程度上影响测试结果的精准性。testing.B 提供了一系列的方法可以方便地控制计时器,从而让计时器只在需要的区间进行测试。我们通过下面的代码来了解计时器的控制
func Benchmark_Add_TimerControl(b *testing.B) {
// 重置计时器
b.ResetTimer()
// 停止计时器
b.StopTimer()
// 开始计时器
b.StartTimer()
var n int
for i := 0; i < b.N; i++ {
n++
}
}
测试覆盖率 #
go test -v -coverprofile cover.out user_test.go user.go
go tool cover -html=cover.out -o cover.html
测试http #
package client
import (
"encoding/json"
"fmt"
"net/http"
)
type UserInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}
func NewUserInfo() *UserInfo {
return &UserInfo{
Name: "Test",
Age: 30,
}
}
func HandleNewUser(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
fmt.Printf("url parameter user name is %s\n", name)
say := r.FormValue("say")
fmt.Printf("req say:' %s '\n", say)
newUser := NewUserInfo()
jData, _ := json.Marshal(newUser)
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
w.Write(jData)
}
func Start() {
http.HandleFunc("/create",HandleNewUser)
http.ListenAndServe(":9090",nil)
}
测试脚本
package client
import (
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
)
func TestHandleNewUser(t *testing.T) {
postBody := url.Values{}
postBody.Add("say", "hello world")
req := httptest.NewRequest(http.MethodPost, "http://localhost:9090/create?name=linus", strings.NewReader(postBody.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
HandleNewUser(w,req)
if w.Code != http.StatusOK {
t.Error("new user api error")
}
if w.Body.Len() == 0 {
t.Error(" response is empty")
}
user := &UserInfo{}
err := json.Unmarshal(w.Body.Bytes(), user)
if err != nil {
t.Error("response data error")
}
t.Logf("create user api response : %#v", user)
}
#主函数,启动服务
package main
import "test/client"
func main() {
client.Start()
}
# go run main.go
#client> go test -v
#output
=== RUN TestHandleNewUser
url parameter user name is linus
req say:' hello world '
server_test.go:31: create user api response : &client.UserInfo{Name:"Test", Age:30}
--- PASS: TestHandleNewUser (0.00s)
PASS
ok test/client 2.541s
使用断言Assert #
go get github.com/stretchr/testify
func TestNewUserInfo(t *testing.T) {
a := assert.New(t)
router := gin.New()
const path = "/newUserInfo"
router.POST(path, NewUserInfo)
body := url.Values{}
body.Set("say", "hello world")
rr, err := testutils.PostFormRequst(path + "?name=lp", router, body)
a.Nil(err)
user := &model.UserInfo{}
err = json.Unmarshal(rr.Body.Bytes(), user)
a.Nil(err)
a.NotEqual(user.Name, "")
a.NotEqual(user.Age, 0)
t.Logf("%#v\n", user)
}
其它库httpexpect库 #
var testurl string = "http://127.0.0.1:9999"
func TestHttpGetPass(t *testing.T) {
e := httpexpect.New(t, testurl) //创建一个httpexpect实例
e.GET("/device"). //ge请求
Expect().
Status(http.StatusOK). //判断请求是否200
JSON().
Object().
ContainsKey("name").
ValueEqual("name", "探针程序")
}
常用测试库 #
github.com/stretchr/testify
github.com/jarcoal/httpmock
github.com/gavv/httpexpect
testify里有assert相信有其他语言基础的同学一定知道他是做什么的,断言处理比如
a.Nil(err)
a.NotEqual(user.Name, "")
a.NotEqual(user.Age, 0)