Go语言入门-工程实践-测试 | 青训营笔记

121 阅读3分钟

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

本堂课重点内容

  • 单元测试:覆盖范围最广,最细的测试
  • Mock测试:使测试程序摆脱依赖从而测试结果更加稳定
  • 基准测试:测试程序性能

1.测试总览

  • 回归测试:通常为人工基于固定流程对系统进行测试,面向主流场景
  • 集成测试:对系统功能维度进行测试验证,通常关联多个子系统或者子模块
  • 单元测试:位于开发阶段,开发人员对单独的函数,模块进行功能验证,下面简称为单测

从上到下,测试成本降低,测试覆盖率上升
结论:单测在一定程度上决定代码质量

2. 测试介绍

2.1 单元测试

单元测试主要用于校对输入经过测试单元得到的输出与期望是否一致,其涵盖的范围包括但不限于函数,模块,接口 image.png

优势:一定程度上保证功能的正确性,并且不对原有代码造成破坏,能够快速对错误进行定位和修复

2.1.1 Go中的单测

涉及标准库:testing

  • 所有测试文件以xxx_test.go结尾(xxx为Go源码文件前缀)
  • 在测试文件中所有测试函数签名为TestXxx(*testing.T)
  • 测试文件中的初始化逻辑位于TestMain(m *testing.M)

TestMain模板

func TestMain(m *tesing.M){
    // 测试前:数据装置、配置初始化
    code := m.Run()
    
    // 测试和:释放资源等
    os.Exit(code)
}

单测运行
go test [flags] [packages]

打印go help test可以获取更多信息,如具体的flags:

image.png

2.1.2外部包协助

可以额外引入外部包协助我们完成单测代码的编写, 如"assert"
完整引入链接为:github.com/stretchr/testify/assert

作用是判断输出与期望是否匹配
assert.Equal(t, expectedOutput, output)

2.1.3 单测覆盖率

go test中的单测覆盖率计算公式为:覆盖率=运行过的代码行数单测范围的代码行数:覆盖率 = \frac{运行过的代码行数}{单测范围的代码行数}

通过为go test添加参数实现输出覆盖率go test function_test.go function.go --cover

image.png

注意点:

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

2.1.4 单元测试-依赖

工程中复杂的项目通常会依赖文件系统,数据库,缓存等,而这些外部依赖输出可能是不稳定的,并且有可能被修改,但是单测是需要保证稳定性幂等性的,因此我们需要Mock机制来实现这一点

比如我们实现一个函数对一个文件内的字符串进行替换操作,当文件的内容被修改后,函数的输出与我们预期的输出可能会不一致,但函数不一定是存在bug的

稳定性:相互隔离,能在任何时间,任何环境,运行测试。
幂等性:是指每一次测试运行都应该产生与之前一样的结果

Mock机制 涉及外部包:Monkey(github.com/bouk/monkey)

原理:

  • 原函数:待打桩的函数
  • 替换函数:打桩函数
  • Patch函数用于实现将打桩函数(replacement)的函数地址替换待打桩函数(target)
  • Unpatch函数作用是移除待打桩函数(target)上的所有打桩函数 image.png

实际应用中,我们可以将从文件,数据库,缓存的读取函数视为待打桩函数(因为它们的结果可能是不稳定的),将我们实现的具有稳定特定输出的函数作为打桩函数,并使用Patch将它们在运行时进行替换

image.png

2.2 基准测试

目标是:优化代码性能,提高执行效率

Go中的基准测试

  • 测试函数签名为BenchmarkXxx(b *testing.B)
  • go test -bench="." 注意windows中要加双引号才能正确表示.,否则会报no tests to run

image.png