51Testing软件测试论坛

标题: 单元测试执行案例之GO语言 [打印本页]

作者: lsekfe    时间: 2023-3-1 10:52
标题: 单元测试执行案例之GO语言
单元测试的概念
  Gopher一定要养成写单元测试的习惯,这样才能保证我们交付代码的质量,同时提升个人开发水平!
  什么是单元测试
  1. 最小的可测试单位,比如函数、对象的某个接口。
  2. 是[url=]软件开发[/url]过程中对最小单位进行正确性验证的测试工作。
  为什么要写单元测试
  1. 保证变更、重构的正确性,特别是在多人协作的项目中。
  2. 简化调试过程:可以快速定位到问题代码。
  3. 单元测试是最好的开发文档:单元测试给出具体函数的使用方法及边界值,是最好的示例代码。
  编写测试用例的原则
  1. 单一原则:一个测试用例只负责一个应用场景
  2. 原子性:结果只有两种情况:Pass/Fail
  3. 优先级:核心的组件、逻辑要优先测试
  4. 公共使用库: utils工具类库要重点覆盖
  Go约定的测试规则
  1. 单元测试的文件需要和被测试的文件在统一目录下,测试用例名称必须是xxx_test.go的格式。
  测试文件中包含三种类型的函数,单元测试函数、基准测试函数和示例函数。

  单元测试的实践过程
  编写一个简单的测试用例
  package service

  import "testing"

  func add(a, b int) int {
  return a + b
  }

  func TestAdd(t *testing.T) {
  a := 10
  b := 20
  wanted := 30
  actual := add(a, b)
  if wanted != actual {
  t.Errorf("[add函数的入参:%d %d] [期望值:%d] [实际结果:%d]", a, b, wanted, actual)
  }
  }


  执行当前单元测试的命令: go test -v .\user_test.go
  PS C:\Program Files\JetBrains\GoRepository\gf-demo-user\internal\service> go test -v .\user_test.go
  === RUN   TestAdd
  --- PASS: TestAdd (0.00s)
  PASS
  ok      command-line-arguments  0.075s


  执行当前目录下所有的测试用例(跳过某个测试函数),并导出到制定的输出文件:go test -v ./... --short=true -coverprofile cover.out
  PS C:\Program Files\JetBrains\GoRepository\gf-demo-user\internal\service> go test -v ./... --short=true -coverprofile cover.out
  === RUN   TestAdd
  --- PASS: TestAdd (0.00s)
  coverage: 0.0% of statements
  ok      github.com/gogf/gf-demo-user/v2/internal/service        0.121s  coverage: 0.0% of statements


  查看输出覆盖率的html文档:go tool cover --func=cover.out
  PS C:\Program Files\JetBrains\GoRepository\gf-demo-user\internal\service> go tool cover --func=cover.out
  github.com/gogf/gf-demo-user/v2/internal/service/biz_ctx.go:22:         BizCtx                  0.0%
  github.com/gogf/gf-demo-user/v2/internal/service/biz_ctx.go:29:         RegisterBizCtx          0.0%
  github.com/gogf/gf-demo-user/v2/internal/service/middleware.go:19:      Middleware              0.0%
  github.com/gogf/gf-demo-user/v2/internal/service/middleware.go:26:      RegisterMiddleware      0.0%
  github.com/gogf/gf-demo-user/v2/internal/service/session.go:21:         Session                 0.0%
  github.com/gogf/gf-demo-user/v2/internal/service/session.go:28:         RegisterSession         0.0%
  github.com/gogf/gf-demo-user/v2/internal/service/user.go:26:            User                    0.0%
  github.com/gogf/gf-demo-user/v2/internal/service/user.go:33:            RegisterUser            0.0%
  total:                                                                  (statements)            0.0%


  真实案例
  被测试的函数
  func Split(s, sub string) (result []string) {
  if len(strings.TrimSpace(s)) == 0 {
  return
  }
  i := strings.Index(s, sub)
  for i > -1 {
  if i == 0 {
  s = s[len(sub):]
  } else {
  result = append(result, s[:i])
  s = s[i+len(sub):]
  }
  i = strings.Index(s, sub)
  }
  result = append(result, s)
  return
  }


  简单的测试
  get := Split("a:b:c", ":")
  wanted := []string{"a", "b", "c"}
  if !reflect.DeepEqual(wanted, get) {
  t.Errorf("expected:%v,get:%v", wanted, get)
  }


  多维度测试案例
  func TestMultiSplit(t *testing.T) {
  type test struct {
  name   string
  input  string
  sub    string
  wanted []string
  }
  tests := []test{
  {name: "正常的测试用例", input: "a:b:c", sub: ":", wanted: []string{"a", "b", "c"}},
  {name: "字符串头位置匹配测试", input: ":a:b:c", sub: ":", wanted: []string{"a", "b", "c"}},
  {name: "无匹配项的测试", input: "abc", sub: ":", wanted: []string{"abc"}},
  {name: "英文串的测试", input: "abcd", sub: "bc", wanted: []string{"a", "d"}},
  {name: "中文串的测试", input: "云原生", sub: "云", wanted: []string{"原生"}},
  }
  for _, d := range tests {
  get := Split(d.input, d.sub)
  if !reflect.DeepEqual(d.wanted, get) {
  t.Errorf("[testName:%v][expected:%v] [get:%v]", d.name, d.wanted, get)
  }
  }
  }


  GoFrame 中依赖框架配置文件的单元测试
  加载测试用例的配置文件
  root.go
  package testingutil

  import (
  "path"
  "runtime"
  )

  func RootDir() string {
  _, d, _, _ := runtime.Caller(0)
  return path.Dir(path.Dir(d))
  }


  root_test.go
  package testingutil

  import "testing"

  func TestRootDir(t *testing.T) {
  rootDir := RootDir()
  t.Log(rootDir)
  }

  func BenchmarkRootDir(b *testing.B) {
  for i := 0; i < b.N; i++ {
  RootDir()
  }
  }


  初始化加载GoFrame配置环境
  config.go
  package testingutil

  import (
  "github.com/gogf/gf/v2/os/genv"
  "path"
  )

  func init() {
  InitConfig()
  }

  func InitConfig() {
  configDir := path.Join(RootDir(), "manifest/config")

  genv.Set("GF_GCFG_PATH", configDir)
  }


  引入环境,并编写mysql、redis的测试用例
  func TestMysql(t *testing.T) {
  testingutil.InitConfig()
  ctx := gctx.New()
  countSql := "select collection_id,count(1) as count from transfer_logs" + " where 1 = 1" + " " + " group by collection_id"

  var countList []struct {
  CollectionId int64 `json:"collection_id"`
  Count        int   `json:"count"`
  }

  // 获取内容
  all, err := g.DB().Ctx(ctx).GetAll(ctx, countSql)
  if err != nil {
  t.Fatal(err)
  }
  all.Structs(&countList)
  fmt.Printf("%+v", countList)
  }

  func TestRedis(t *testing.T) {
  testingutil.InitConfig()
  ctx := gctx.New()
  cache := gcache.NewAdapterRedis(g.Redis())
  err := cache.Set(ctx, 1, 1, time.Second*10)
  if err != nil {
  t.Fatal(err)
  }
  }








欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2