入门 Go 语言-工程实战| 青训营笔记

70 阅读2分钟

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

入门 Go 语言-测试

测试

回归测试,集成测试,单元测试

从左到右,覆盖率逐层变大,成本却逐层降低

单元测试-规则

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

单元测试-例子

package main

import "testing"

func HelloTom() string {
   return "Jerry"
}
func TestHelloTom(t *testing.T) {
   output := HelloTom()
   expectOutput := "Tom"
   if output != expectOutput {
      t.Errorf("Expected %s do not match actual %s", expectOutput, output)
   }
}

单元测试-覆盖率

package test

func JudgePassLine(score int16) bool {
   if score >= 60 {
      return true
   }
   return false
}
package test

import (
   "testing"
)

func TestJudgePassLineTrue(t *testing.T) {
   expectOutput := true
   output := JudgePassLine(70)
   if output != expectOutput {
      t.Errorf("Expected %t do not match actual %t", expectOutput, output)
   }
}
  • 执行语句go test xxxt.go xxx.go --cover返回结果ok command-line-arguments 0.588s coverage: 66.7% of statements
  • 我们发现覆盖于只有66.7%是因为只测试了大于60的分支部分没有测试小于60的下面我们添加测试代码
package test

import (
   "testing"
)

func TestJudgePassLineTrue(t *testing.T) {
   expectOutput := true
   output := JudgePassLine(70)
   if output != expectOutput {
      t.Errorf("Expected %t do not match actual %t", expectOutput, output)
   }
}
func TestJudgePassLineFail(t *testing.T) {
   expectOutput := false
   output := JudgePassLine(50)
   if output != expectOutput {
      t.Errorf("Expected %t do not match actual %t", expectOutput, output)
   }
}

再次执行go test xxxt.go xxx.go --cover我们发现覆盖率已经到达了100%ok command-line-arguments 0.634s coverage: 100.0% of statements

单元测试-Tips

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

单元测试-依赖

外部依赖=>稳定&幂等

单元测试-文件处理

package test

import (
   "bufio"
   "os"
   "strings"
)

func ReadFirstLine() string {
   open, err := os.Open("log")
   if err != nil {
      return ""
   }
   scanner := bufio.NewScanner(open)
   for scanner.Scan() {
      return scanner.Text()
   }
   return ""
}
func ProcseeFirstLine() string {
   line := ReadFirstLine()
   destLine := strings.ReplaceAll(line, "11", "00")
   return destLine
}

单元测试

package test

import (
   "github.com/stretchr/testify/assert"
   "testing"
)

func TestProcseeFirstLine(t *testing.T) {
   firstLine := ProcseeFirstLine()
   assert.Equal(t, "line00", firstLine)
}

单元测试结果=== RUN TestProcseeFirstLine--- PASS: TestProcseeFirstLine (0.00s)

单元测试-Mock

这里我们对ReadFirstLine打桩测试,不依赖本地文件

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

基准测试

  • 优化代码,需要对当前代码分析
  • 内置的测试框架提供了基准测试的能力
package test

import "math/rand"

var ServerIndex [10]int

func InitServerIndex() {
   for i := 0; i < 10; i++ {
      ServerIndex[i] = i + 100
   }
}
func Select() int {
   return ServerIndex[rand.Intn(10)]
}

Select进行基准测试

package test

import "testing"

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()
      }
   })
}

测试结果

  • BenchmarkSelect-12 68482987 18.07 ns/op
  • BenchmarkSelectParallel-12 16939533 69.95 ns/op

基准测试-优化

[rand.Intn(10)]改为更快的[fastrand.Intn(10)]

func FastSelect() int {
   return ServerIndex[fastrand.Intn(10)]
}

再次测试

func BenchmarkFastSelectParallel(b *testing.B) {
   InitServerIndex()
   //重置定时器
   b.ResetTimer()
   b.RunParallel(func(pb *testing.PB) {
      for pb.Next() {
         FastSelect()
      }
   })
}
  • BenchmarkSelectParallel-12 22332769 64.60 ns/op
  • BenchmarkFastSelectParallel-12 22082407 51.19 ns/op