51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 3065|回复: 3
打印 上一主题 下一主题

[转贴] 在Gin中该如何优雅地写单元测试

[复制链接]
  • TA的每日心情
    无聊
    5 天前
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2021-8-12 11:35:38 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
    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 函数 :
    1. package tests

    2. import (
    3. "testing"
    4. "fmt"
    5. "os"
    6. "github.com/gin-gonic/gin"
    7. "go-api/config"
    8. )

    9. func setup() {
    10. gin.SetMode(gin.TestMode)
    11. fmt.Println(config.AppSetting.JwtSecret);
    12. fmt.Println("Before all tests")
    13. }

    14. func teardown() {
    15. fmt.Println("After all tests")
    16. }
    17. func TestMain(m *testing.M)  {
    18. setup()
    19.     fmt.Println("Test begins....")
    20. code := m.Run() // 如果不加这句,只会执行Main
    21. teardown()
    22. os.Exit(code)
    23. }
    复制代码
     gin 单元测试案例
    1. package main

    2. func setupRouter() *gin.Engine {
    3. r := gin.Default()
    4. r.GET("/ping", func(c *gin.Context) {
    5. c.String(200, "pong")
    6. })
    7. return r
    8. }

    9. func main() {
    10. r := setupRouter()
    11. r.Run(":8080")
    12. }
    复制代码
    1. package main

    2. import (
    3. "net/http"
    4. "net/http/httptest"
    5. "testing"

    6. "github.com/stretchr/testify/assert"
    7. )

    8. func TestPingRoute(t *testing.T) {
    9. router := setupRouter()

    10. w := httptest.NewRecorder()
    11. req, _ := http.NewRequest("GET", "/ping", nil)
    12. router.ServeHTTP(w, req)

    13. assert.Equal(t, 200, w.Code)
    14. assert.Equal(t, "pong", w.Body.String())
    15. }
    复制代码
    4、gin该如何优雅的写单元测试

      使用Table-Driven Test
    1. func TestFib(t *testing.T) {
    2.     var fibTests = []struct {
    3.         in       int // input
    4.         expected int // expected result
    5.     }{
    6.         {1, 1},
    7.         {2, 1},
    8.         {3, 2},
    9.         {4, 3},
    10.         {5, 5},
    11.         {6, 8},
    12.         {7, 13},
    13.     }

    14.     for _, tt := range fibTests {
    15.         actual := Fib(tt.in)
    16.         if actual != tt.expected {
    17.             t.Errorf("Fib(%d) = %d; expected %d", tt.in, actual, tt.expected)
    18.         }
    19.     }
    20. }
    复制代码
    封装一些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)
      来控制嵌套测试和执行顺序、解决测试套件依赖性。
    1. func TestFoo(t *testing.T) {
    2.     // <setup code>
    3.     t.Run("A=1", func(t *testing.T) { ... })
    4.     t.Run("A=2", func(t *testing.T) { ... })
    5.     t.Run("B=1", func(t *testing.T) { ... })
    6.     // <tear-down code>
    7. }
    复制代码
    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
    1. func BenchmarkMakeSliceWithoutAlloc(b *testing.B)
    2. func BenchmarkMakeSliceWithPreAlloc(b *testing.B)
    3. func BenchmarkSetBytes(b *testing.B)
    复制代码
    -agrs
    1. func TestArgs(t *testing.T) {
    2.     if !flag.Parsed() {
    3.         flag.Parse()
    4.     }

    5.     argList := flag.Args() // flag.Args() 返回 -args 后面的所有参数,以切片表示,每个元素代表一个参数
    6.     for _, arg := range argList {
    7.         if arg == "cloud" {
    8.             t.Log("Running in cloud.")
    9.         }else {
    10.             t.Log("Running in other mode.")
    11.         }
    12.     }
    复制代码





    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-26 05:05 , Processed in 0.068293 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表