gin单元测试真香实践

发表于:2020-8-24 14:24

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:任性    来源:掘金

  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

funcsetupRouter() *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"
)

funcTestPingRoute(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>
}


  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号