TA的每日心情 | 无聊 4 天前 |
---|
签到天数: 1050 天 连续签到: 1 天 [LV.10]测试总司令
|
1、单元测试常识
·单元测试文件名必须为xxx_test.go(其中xxx为业务逻辑程序)
·分为基础测试、基准测试、案例测试
·基础测试的函数名必须为Testxxx(xxx可用来识别业务逻辑函数)
·基准测试必须以BenchmarkXXX函数名出现
·案例测试必须要已ExampleXXX函数名出线
·单元测试函数参数必须为t *testing.T(测试框架强要求)
·测试程序和被测试程序文件在一个包package中(可以不放,我自己会单独放一个tests目录)
·测试用例文件不会参与正常源码编译,不会被包含到可执行文件中
·必须import testing这个包
2、为什么要写单元测试
保证代码的质量,保证每个函数是可运行,运行结果是正确的,保证写出来的代码性能是好的、同时使用单元测试很直观的可以提高开发效率,你不需要每次编译你的代码, 然后在去postman模拟参数测试、单元可以可以模拟http请求,并且可以帮助你进行性能测试以及代码覆盖率,导出测试报告等等
3、在gin框架中单元测试该如何写
TestMain
在写测试时,有时需要在测试之前或之后进行额外的设置(setup)或拆卸(teardown);有时,测试还需要控制在主线程上运行的代码。为了支持这些需求,testing 包提供了 TestMain 函数 :
- package tests
- import (
- "testing"
- "fmt"
- "os"
- "github.com/gin-gonic/gin"
- "go-api/config"
- )
- func setup() {
- gin.SetMode(gin.TestMode)
- fmt.Println(config.AppSetting.JwtSecret);
- fmt.Println("Before all tests")
- }
- func teardown() {
- fmt.Println("After all tests")
- }
- func TestMain(m *testing.M) {
- setup()
- fmt.Println("Test begins....")
- code := m.Run() // 如果不加这句,只会执行Main
- teardown()
- os.Exit(code)
- }
复制代码 gin 单元测试案例
- package main
- func setupRouter() *gin.Engine {
- r := gin.Default()
- r.GET("/ping", func(c *gin.Context) {
- c.String(200, "pong")
- })
- return r
- }
- func main() {
- r := setupRouter()
- r.Run(":8080")
- }
复制代码- package main
- import (
- "net/http"
- "net/http/httptest"
- "testing"
- "github.com/stretchr/testify/assert"
- )
- func TestPingRoute(t *testing.T) {
- router := setupRouter()
- w := httptest.NewRecorder()
- req, _ := http.NewRequest("GET", "/ping", nil)
- router.ServeHTTP(w, req)
- assert.Equal(t, 200, w.Code)
- assert.Equal(t, "pong", w.Body.String())
- }
复制代码 4、gin该如何优雅的写单元测试
使用Table-Driven Test
- func TestFib(t *testing.T) {
- var fibTests = []struct {
- in int // input
- expected int // expected result
- }{
- {1, 1},
- {2, 1},
- {3, 2},
- {4, 3},
- {5, 5},
- {6, 8},
- {7, 13},
- }
- for _, tt := range fibTests {
- actual := Fib(tt.in)
- if actual != tt.expected {
- t.Errorf("Fib(%d) = %d; expected %d", tt.in, actual, tt.expected)
- }
- }
- }
复制代码 封装一些test helper
package tests
import (
"io"
"net/http"
"net/http/httptest"
"bytes"
"fmt"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"go-api/routes"
"go-api/tool"
)
type TestCase struct {
code int //状态码
param string //参数
method string //请求类型
desc string //描述
showBody bool //是否展示返回
errMsg string //错误信息
url string //链接
content_type string //
ext1 interface{} //自定义1
ext2 interface{} //自定义2
}
func NewBufferString(body string) io.Reader {
return bytes.NewBufferString(body)
}
func PerformRequest(mothod, url, content_type string, body string) (c *gin.Context, r *http.Request, w *httptest.ResponseRecorder) {
router := routes.InitRouter()
w = httptest.NewRecorder()
c, _ = gin.CreateTestContext(w)
r = httptest.NewRequest(mothod, url, NewBufferString(body))
c.Request = r
c.Request.Header.Set("Content-Type", content_type)
router.ServeHTTP(w, r)
return
}
func call(t *testing.T,testcase []TestCase){
for k, v := range testcase {
_, _, w := PerformRequest(v.method, v.url, v.content_type, v.param)
//assert.Contains(t, w.Body.String(),fmt.Sprintf("\"error_code\":%d",v.code))
fmt.Println()
fmt.Printf("第%d个测试用例:%s", k+1, v.desc)
if v.showBody {
fmt.Printf("接口返回%s", w.Body.String())
fmt.Println()
}
s := struct {
Error_code int `json:"error_code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}{}
err := tool.JsonToStruct([]byte(w.Body.String()), &s)
assert.NoError(t, err)
assert.Equal(t, v.errMsg, s.Msg, "错误信息不一致")
assert.Equal(t, v.code, s.Error_code, "错误码不一致")
}
}
使用子测试(subtests)
来控制嵌套测试和执行顺序、解决测试套件依赖性。
- func TestFoo(t *testing.T) {
- // <setup code>
- t.Run("A=1", func(t *testing.T) { ... })
- t.Run("A=2", func(t *testing.T) { ... })
- t.Run("B=1", func(t *testing.T) { ... })
- // <tear-down code>
- }
复制代码 5、go test命令
-bench regexp 执行相应的 benchmarks,例如 -bench=.;
-benchtime s 指定性能测试时间,默认一秒
-cover 开启测试覆盖率;
-run regexp 只运行 regexp 匹配的函数,例如 -run=Array 那么就执行包含有 Array 开头的函数;
-v 显示测试的详细命令。
-cache=1 去除缓存
-args 把-args后面的参数带到测试中去
-json 把输出转成json
-o -o 参数指定生成的二进制可执行程序,并执行测试,测试结束不会删除该程序
-count 指定执行次数;默认1次
-bench
- func BenchmarkMakeSliceWithoutAlloc(b *testing.B)
- func BenchmarkMakeSliceWithPreAlloc(b *testing.B)
- func BenchmarkSetBytes(b *testing.B)
复制代码 -agrs
- func TestArgs(t *testing.T) {
- if !flag.Parsed() {
- flag.Parse()
- }
-
- argList := flag.Args() // flag.Args() 返回 -args 后面的所有参数,以切片表示,每个元素代表一个参数
- for _, arg := range argList {
- if arg == "cloud" {
- t.Log("Running in cloud.")
- }else {
- t.Log("Running in other mode.")
- }
- }
复制代码
|
|