Go测试 | 青训营

100 阅读3分钟

单元测试

基本知识

  1. 单元测试出现在开发阶段
  2. 单元测试的测试对象是各个函数、模块、接口等
  3. 单元测试规则
    1. 所有测试文件以_test.go结尾
    2. 测试方法模式为:func TestXxx(t *testing.T){}
    3. TestMain方法
func TestMain(m *testing.M){
    //测试前:数据装配、配置初始化等前置工作(如果是简单测试,则无需此方法)
    code:=m.Run()
    //测试后:释放资源等收尾工作
    os.Exit(code)
}
  1. 测试命令go test xx_test.go
  2. 单元测试举例

屏幕截图 2023-08-01 230336.png
屏幕截图 2023-08-01 230511.png

高级部分

assert——断言

  1. assert.Equal(t, expectOutput, output)常用于期望值与测试值的比较
    • 结果符合 image.png
    • 结果不符合,则抛出错误,显示期待值和实际值
  2. 在新的文件夹下 创建,终端使用go mod init创建go.mod文件管理,
  3. 导包 文件中import"github.com/stretchr/testify/assert",终端使用go get github.com/stretchr/testify/assert导包
  4. 如下图

屏幕截图 2023-08-02 150641.png

覆盖率

  1. 覆盖率指的是在测试时,被测试方法中已执行的代码/总代码
  2. 覆盖率越高越好
  3. 获得测试覆盖率的命令go test xx_test.go xx.go --cover
  4. 测试案例

image.png
image.png

tips

  1. 一般覆盖率:50-60%
  2. 较高覆盖率:80%+
  3. 测试分支相互独立、全面覆盖
  4. 测试单元粒度小,函数职责/功能单一

依赖+Mock

  1. 单元测试往往会依赖一些外部组件,如数据库、中间件(缓存)、文件等等
  2. 单元测试往往需要达到两个目标:幂等+稳定
    1. 幂等:(幂)多次运行程序,结果一致(等)
    2. 稳定:单元测试相互隔离,任何函数均可独立运行

为了使单元测试能够满足 幂等+稳定 的需求,又能够避免对外部组件的强依赖,引入Mock机制

  1. Mock机制常用包:bou.ke/monkey
  2. Mock用法:为一个(目标)函数打桩(替换)
  3. 对比强依赖:Mock机制主要是让方法不依赖外部文件,可以在任何地方进行单元测试
  4. 举例说明
package main

import (
	"bufio"
	"os"
	"strings"
	"testing"
	"bou.ke/monkey"
	"github.com/stretchr/testify/assert"
)

func ReadFirstLine() string {
	open, err := os.Open("log.txt")
	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
}

func TestProcessFirstLine(t *testing.T) {
	// 普通依赖方法(ReadFirstLine依赖于log文件)
	// line := ProcessFirstLine()
	// assert.Equal(t, "line00", line)

	//使用mock机制
    //打桩
	monkey.Patch(ReadFirstLine, func() string {
		return "line110"
	})
    //去除桩
	defer monkey.Unpatch(ReadFirstLine)
	line := ProcessFirstLine()
	assert.Equal(t, "line000", line)
}

ReadFirstLine——打开log.txt文件,读取第一行返回(依赖外部文件)
ProcessFirstLine——对获取到的字符串进行替换处理
TestProcessFirstLine——测试ProcessFirstLine

  1. 在上述案例中,mock机制作用在对外部文件强依赖的ReadFirstLine上,使其对ProcessFirstLine的输入由测试者控制,在不修改ProcessFirstLineReadFirstLine的前提下,解除了待测试方法ProcessFirstLine对外部文件的依赖

基准测试

  1. 基准测试的目的:测试性能,性能优化测试
  2. 基准测试命令:go test -bench=.
  3. 案例演示
package main

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(p *testing.PB) {
		for p.Next() {
			Select()
		}
	})
}

结果如下:并行协程运算速度会比直接逐个运行慢
image.png

  1. 案例优化——提高select()的执行速率,可以加快并行协程运行速度
    1. 导入依赖"github.com/bytedance/gopkg/lang/fastrand"
    2. 使用fastrand优化速度,使用FastSelect()替代Select()
func FastSelect() int {
	return ServerIndex[fastrand.Intn(10)]
}
  1. 结果如下

image.png

总结

所有的测试均最好在独立的文件夹下,如果需要导入外部依赖则需要创建go.mod文件,而该文件会限制所在文件夹内不许出现同名函数,而且外部依赖需要使用go get 下载。
所有的测试案例自己打一遍后,才会真正掌握,因为有很多细节是需要自己去试着解决的,ppt上的内容和老师讲的并不能覆盖所有可能遇到的问题,何况自己实践过后印象总会更深一点。