GO语言单元测试| 青训营笔记

114 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天

课堂笔记

本堂课重点内容

  • 单元测试
  • Mock测试
  • 基准测试

详细知识点介绍

##单元测试

流程

输入->测试单元->输出->校对

规则

  • 所有测试文件以_test.go结尾
  • func TestXxx(*testing.T)
  • 初始化逻辑放到TestMain中

案例

 package test06
 ​
 func HelloTom() string {
     return "Jerry"
 }
 ​
 package test06
 ​
 import "testing"
 ​
 func TestHelloTom(t *testing.T) {
     output := HelloTom()
     expectOutput := "Tom"
     if output != expectOutput {
         t.Errorf("Expected: %s, Got: %s", expectOutput, output)
     }
 }
结果

=== RUN TestHelloTom HelloTom_test.go:9: Expected: Tom, Got: Jerry --- FAIL: TestHelloTom (0.00s)

覆盖率

  • 如何衡量代码是否经过了足够的测试?
  • 如何评价项目的测试水准?
  • 如何评估项目是否达到了高水准测试等级?

计算路过了代码的多少行,比如下面的样例就在代码第二行的位置return了,代码有效行数为3,所以覆盖率为2/3=66.7%

  • 可以通过不断改变测试条件,让测试覆盖率较高即可
样例
 package test06
 ​
 func JudgePassLine(socre int16) bool {
     if socre >= 60 {
         return true
     } else {
         return false
     }
 }
 package test06
 ​
 import (
     "github.com/stretchr/testify/assert"
     "testing"
 )
 ​
 func TestJudgePassLineTrue(t *testing.T) {
     isPass := JudgePassLine(70)
     assert.Equal(t, true, isPass)
 }
结果

command-line-arguments 0.237s coverage: 66.7% of statements

重点

  • 一般覆盖率:50%-60%;较高覆盖率80%+
  • 测试分支相互独立、全面覆盖
  • 测试单元粒度足够小,函数单一职责

依赖

  • 单元

    • File
    • DB
    • Cache

  • Mock

    • 幂等
    • 稳定(任何函数在任何时间段独立运行)

Mock测试

  • 为一个函数打桩
  • 为一个方法打桩
优点

不用依赖本地文件

 func TestProcessFirstLineWithMock(t *testing.T) {
     monkey.Patch(ReadFirstLine, func() string {
         return "line110"
     })
     defer monkey.Unpatch(ReadFirstLine)
     line := ProcessFirstLine()
     assert.Equal(t, "line000", line)
 }

基准测试

  • 优化代码,需要对当前代码分析

  • 内置的测试框架提供了基准测试的能力

    • 使用了第三方SDK
    • 降低了并发的性能

实践练习例子

基准测试

 package test06
 ​
 import (
     "math/rand"
     "testing"
 )
 ​
 var ServerIndex [10]int
 ​
 func InitServerIndex() {
     for i := 0; i < 10; i++ {
         ServerIndex[i] = i + 100
     }
 }
 func Select() int {
     return ServerIndex[rand.Intn(10)]
 }
 func BenchmarkSelect(b *testing.B) {
     InitServerIndex()
     b.ResetTimer()
     for i := 0; i < b.N; i++ {
         Select()
     }
 }
 func BenchmarkSelectParallel(b *testing.B) {
     InitServerIndex()
     b.ResetTimer()
     b.RunParallel(func(pb *testing.PB) {
         for pb.Next() {
             Select()
         }
     })
 }
 ​

文件处理

下文普通测试时是没有问题的,但是一旦测试文件(“log”)被修改,就会出错

 package test06
 ​
 import (
     "bufio"
     "os"
     "strings"
 )
 ​
 func ReadFirstLine() string {
     open, err := os.Open("log")
     defer open.Close()
     if err != nil {
         return ""
     }
     scanner := bufio.NewScanner(open)
     for scanner.Scan() {
         return scanner.Text()
     }
     return ""
 }
 func ProcessFirstLine() string {
     line := ReadFirstLine()
     destLine := strings.ReplaceAll(line, "11", "00")
     return destLine
 }
 ​
 package test06
 ​
 import (
     "github.com/stretchr/testify/assert"
     "testing"
 )
 ​
 func TestReadFirstLine(t *testing.T) {
     firstLine := ProcessFirstLine()
     assert.Equal(t, "line00", firstLine)
 }
 ​

课后个人总结

  • 单元测试简单,但是需要将覆盖率提高来保证全部函数的覆盖
  • Mock测试需要引入第三方包,不会自动引入