51Testing软件测试论坛

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

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 3740|回复: 0
打印 上一主题 下一主题

[转贴] gin单元测试真香实践

[复制链接]
  • TA的每日心情
    无聊
    昨天 09:14
  • 签到天数: 938 天

    连续签到: 5 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2020-8-27 10:19:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    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.    
    7.     "github.com/gin-gonic/gin"
    8.    
    9.     "go-api/config"
    10. )
    11. func setup() {
    12.     gin.SetMode(gin.TestMode)
    13.     fmt.Println(config.AppSetting.JwtSecret);
    14.     fmt.Println("Before all tests")
    15. }
    16. func teardown() {
    17.     fmt.Println("After all tests")
    18. }
    19. func TestMain(m *testing.M)  {
    20.     setup()
    21.     fmt.Println("Test begins....")
    22.     code := m.Run() // 如果不加这句,只会执行Main
    23.     teardown()
    24.     os.Exit(code)
    25. }
    复制代码
    gin 单元测试案例
    1.  package main

    2. funcsetupRouter() *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. }

    13. package main

    14. import (
    15.    "net/http"
    16.     "net/http/httptest"
    17.     "testing"

    18.     "github.com/stretchr/testify/assert"
    19. )

    20. funcTestPingRoute(t *testing.T) {
    21.     router := setupRouter()

    22.     w := httptest.NewRecorder()
    23.     req, _ := http.NewRequest("GET","/ping", nil)
    24.     router.ServeHTTP(w, req)

    25.     assert.Equal(t, 200, w.Code)
    26.     assert.Equal(t,"pong", w.Body.String())
    27. }
    复制代码
    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
    1. package tests

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

    6.     "bytes"
    7.     "fmt"
    8.     "testing"

    9.     "github.com/gin-gonic/gin"
    10.     "github.com/stretchr/testify/assert"

    11.     "go-api/routes"
    12.     "go-api/tool"
    13. )

    14. type TestCase struct {
    15.     code         int         //状态码
    16.     param        string      //参数
    17.     method       string      //请求类型
    18.     desc         string      //描述
    19.     showBody     bool        //是否展示返回
    20.     errMsg       string      //错误信息
    21.     url          string      //链接
    22.     content_type string      //
    23.     ext1         interface{} //自定义1
    24.     ext2         interface{} //自定义2
    25. }

    26. func NewBufferString(body string) io.Reader {
    27.     return bytes.NewBufferString(body)
    28. }

    29. func PerformRequest(mothod, url, content_type string, body string) (c *gin.Context, r *http.Request, w *httptest.ResponseRecorder) {
    30.     router := routes.InitRouter()
    31.     w = httptest.NewRecorder()
    32.     c, _ = gin.CreateTestContext(w)
    33.     r = httptest.NewRequest(mothod, url, NewBufferString(body))
    34.     c.Request = r
    35.     c.Request.Header.Set("Content-Type", content_type)
    36.     router.ServeHTTP(w, r)
    37.     return
    38. }


    39. func call(t *testing.T,testcase []TestCase){
    40.     for k, v := range testcase {
    41.         _, _, w := PerformRequest(v.method, v.url, v.content_type, v.param)
    42.         //assert.Contains(t, w.Body.String(),fmt.Sprintf("\"error_code\":%d",v.code))
    43.         fmt.Println()
    44.         fmt.Printf("第%d个测试用例:%s", k+1, v.desc)
    45.         if v.showBody {
    46.             fmt.Printf("接口返回%s", w.Body.String())
    47.             fmt.Println()
    48.         }

    49.         s := struct {
    50.             Error_code int         `json:"error_code"`
    51.             Msg        string      `json:"msg"`
    52.             Data       interface{} `json:"data"`
    53.         }{}
    54.         err := tool.JsonToStruct([]byte(w.Body.String()), &s)
    55.         assert.NoError(t, err)
    56.         assert.Equal(t, v.errMsg, s.Msg, "错误信息不一致")
    57.         assert.Equal(t, v.code, s.Error_code, "错误码不一致")
    58.     }
    59. }
    复制代码
     使用子测试(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. }
    复制代码







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

    使用道具 举报

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-27 05:30 , Processed in 0.069923 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

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