Go语言的测试 | 青训营笔记

121 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天 文章将介绍在项目开发过程中如何编写测试用例

测试类型

测试可分为回归测试、集成测试以及单元测试。

  • 回归测试一般为用户亲自操作进行测试,例如手动鼠标点击或屏幕滑动
  • 集成测试一般为专业测试人员使用编写的测试脚本进行自动化测试
  • 单元测试需要开发人员在项目开发中进行重点编写

单元测试

  1. 编写单元测试文件应与需要被测试文件的同包以"_test"结尾
  2. 单元测试内的函数应以Test开始以被测试的函数名结尾,且被测试函数名首字母应大写
  3. 测试单元可为简单函数或模块函数
  4. 单元测试应保证分支独立、全面覆盖,测试单元密度足够小,函数单一职责

以下为一个add函数的测试单元

// my_test.go
func TestAdd(t *testing.T) {
   ans := add(12, 34)
   exp := 46
   if ans != exp {
      t.Error("答案错误")
   }
}

使用assert可简化为

func TestAdd(t *testing.T) {
   assert.Equal(t, 46,add(12, 34), "答案错误")
}

测试覆盖率

测试覆盖率可反映测试编写的是否全面,是用来度量测试完整性的一个手段,通过覆盖率可反映代码是否经过的足够的测试。其中一般代码覆盖率应达到50-60%,关键代码如支付应达到80%,计算方法可理解为经过测试的代码行数/总代码行数

// my.go
func add(a, b int) int  {
   return a + b
}
func bijiao(a, b int) int {
   if a > b {
      return 1
   } else {
      return -1
   }
}
// my_test.go
func TestAdd(t *testing.T) {
   assert.Equal(t, 122,add(6,6))
}
// 用于保证测试的全面
func TestBijiao1(t *testing.T) {
   assert.Equal(t, -1, bijiao(12,34))
}
func TestBijiao2(t *testing.T) {
   assert.Equal(t, 1, bijiao(62,34))
}

分析:bijiao函数内有一个if分支,若只对a>b进行测试则无法对bijiao函数测试全面,所以应对每一个分支都进行测试。类比到真实运行的系统,测试覆盖率较低则不能保证系统能够正常运行。

mock测试

mock测试目标是让用户在单元测试中低成本的完成打桩,mock通过"虚拟"代码替换掉依赖的方法和资源,减少对网络、文件或者数据库的依赖。

文章使用了monkey做mock测试,它在运行时通过汇编语言重写可执行文件,将目标函数或方法的实现跳转到桩实现,其原理类似于热补丁。

注意事项:

  • monkey不支持内联函数,在测试的时候需要通过命令行参数-gcflags=-l关闭Go语言的内联优化。
  • monkey不是线程安全的,所以不能把它用到并发的单元测试中。
func readFile() string {
   return readLine()
}
func readLine() string {
   return "qwqwe1233"
}

防止内联优化执行 go test -run=TestFile -v -gcflags=-l

func TestFile(t *testing.T) {
   monkey.Patch(readLine, func() string {
      return "asda456"
   })
   defer monkey.Unpatch(readLine)
   ss := readFile()
   assert.Equal(t, "qwqwe1233", ss)
}

测试结果为 expected: "qwqwe1233" actual : "asda456"

基准测试

基准测试就是在一定的工作负载之下检测程序性能的一种方法

  • 基准测试以Benchmark开始,通过-bench=.标记来执行该包下的所有的基准函数
  • 参数类型为b *testing.B
  • 基准测试必须要执行b.N次
    • 使用for进行串行执行
    • 使用b.RunParallel进行并行执行
// my.go
var index [10]int
func initIndex()  {
   for i := 0; i < 10; i++ {
      index[i] = i
   }
}
func selectIndex() int {
   return index[rand.Intn(10)]
}
// my_test.go
func BenchmarkSelect1(b *testing.B) {
   initIndex()
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      selectIndex()
   }
}
func BenchmarkSelect2(b *testing.B) {
   initIndex()
   b.ResetTimer()
   b.RunParallel(func(pb *testing.PB) {
     for pb.Next() {
       selectIndex()
     }
   })
}

测试结果: image.png

文章引用的开源包

  • testify:go get -u github.com/stretchr/testify
  • monkey:go get -u go get bou.ke/monkey

参考文章