go-单元测试
前言
零基础小白可放心食用!有很多概念解释比如匹配or通配符等等,一起加油!
学习来源:
Go 的单元测试是 Go 语言中内置的一个重要功能,支持开发者快速验证代码的正确性。通过标准库 testing 和相关工具(如 go test),你可以编写简洁、高效的单元测试用例。
单元测试基本概念
- 单元测试是指对代码中最小的可测试部分(如函数或方法)进行验证,以确保其行为符合预期。
- 在 Go 中,单元测试文件以 _test.go 为后缀,测试函数以 Test 开头。
- Go 的测试框架是内置的,运行 go test 命令即可执行测试。
一个简单例子
Go 语言推荐测试文件和源代码文件放在一块,测试文件以 _test.go 结尾。比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 Add 和 Mul 函数,那么应该新建 calc_test.go 作为测试文件。
1 | example/ |
例如calc.go代码如下
1 | package main |
则在同一个包中的calc_test.go可以这么写
1 | package main |
- 文件名必须以 _test.go 结尾。
- 测试函数:
- 名称必须以 Test 开头,例如 TestAdd。
- 参数为 *testing.T 类型,用于报告测试失败和日志。
- 测试文件和函数需要在同一个包中。
*testing.T 是用于单元测试的参数类型,表示测试上下文。它提供了一系列方法,用于报告测试状态、记录日志、跳过测试等。
testing.B 是基准测试的参数类型,表示基准测试上下文。基准测试用于衡量代码的性能,通过* testing.B 提供的方法控制基准测试的运行。*
*testing.M 是 TestMain 函数的参数类型,用于控制测试的整个生命周期。TestMain 是所有测试的入口点,可以在运行测试前后进行全局的初始化和清理工作。
运行go test,改package下所有测试都会被运行
1 | $ go test |
覆盖率
衡量测试用例对代码检查程度的指标,表示被测试的代码中实际被执行的部分占整个代码的比例。Go 提供了内置工具来分析测试覆盖率,通过 -cover 参数可以快速查看。
覆盖率通常以百分比表示,用来评估测试的有效性。测试覆盖率有以下几种类型:
- 语句覆盖率(Statement Coverage):被执行的代码语句占总语句数的比例。
- 分支覆盖率(Branch Coverage):被测试的代码中,分支条件(如 if 和 else)被覆盖的比例。
- 函数覆盖率(Function Coverage):被调用的函数占总函数数的比例。
在 Go 中,-cover 参数默认显示语句覆盖率。
如果只想运行其中的一个用例,例如 TestAdd,可以用 -run 参数指定,该参数支持通配符 *,和部分正则表达式,例如 ^、$。
1 | $ go test -run TestAdd -v |
匹配,通配符与正则表达式
- 匹配:在 Go 的单元测试中,-run 参数用于指定运行哪些测试用例,通过匹配测试函数的名称来决定哪些测试会被执行。具体来说,根据你提供的通配符或正则表达式,与测试函数的名字进行对比。如果函数的名字符合你给定的模式,那么该测试用例会被运行。
匹配的种类:
运行指定测试
运行TestAddgo test -run=TestAdd
使用通配符
- 运行所有以Test开头的测试
go test -run=Test*
- 运行所有以Test开头的测试
使用正则表达式
运行以 Test 开头且包含 Add 或 Subtract 的测试函数go test -run=^Test(Add|Subtract)$
子测试
在 Go 中,子测试是一种强大的功能,可以在一个测试函数中组织和运行多个相关的子测试。通过使用子测试,你可以对一组逻辑相关的测试进行分组,并更清晰地描述每个测试的意图。这种功能通常用在参数化测试或者复杂场景的分层测试中。
基本概念
子测试是通过 t.Run(name, func(t *testing.T)) 创建的:
- name:为子测试指定一个描述性名称,用于标识这个测试。
- 匿名函数 func(t *testing.T):定义子测试的逻辑。
- 子测试会在父测试中运行,可以嵌套多层,形成类似层级结构的测试组织方式。
用法
参数化测试
1 | package main |
运行go test -v
1 | === RUN TestAdd |
嵌套子测试
1 | func TestMath(t *testing.T) { |
帮助函数
Go 的 testing 包提供了一个特殊的函数 t.Helper(),可以用来标记一个函数是测试的帮助函数,从而在测试失败时,错误信息中忽略这个帮助函数的调用栈,直接指向调用帮助函数的实际测试函数。
无帮助函数示例
1 | package main |
含有帮助函数示例
1 | func checkAdd(t *testing.T, a, b, expected int) { |
setup和teardown
在 Go 的测试中,Setup 和 Teardown 是两个常见的概念,用于在测试开始之前执行初始化工作(Setup),以及在测试结束之后进行清理工作(Teardown)。
1 | func setup() { |
- 集中控制测试环境:
TestMain 允许在单个位置控制测试的初始化和清理逻辑,而无需在每个测试中手动处理。 - 确保环境一致性:
使用 setup 和 teardown 可以确保所有测试运行在一个受控的环境中,避免因测试环境问题导致的不一致结果。 - 可扩展性:
如果需要增加更多的全局操作(如日志记录或配置加载),可以很方便地在 setup 和 teardown 中添加。
基准测试
Go 中的基准测试(Benchmarking)用于评估代码的性能,帮助开发者了解程序在执行时的效率和优化空间。与常规的单元测试不同,基准测试主要关注执行时间和性能瓶颈的定位,通过 testing 包中的 testing.B 类型来实现。testing.B 提供了一些方法和属性,可以用于测量函数的执行时间。
1 | package main |
- BenchmarkAdd 是基准测试函数。基准测试函数必须以 Benchmark 开头,并接收一个 *testing.B 类型的参数。
- b.N 是基准测试的循环次数。testing.B 会根据需要调整这个值,以确保测试的执行时间足够长,能准确地测量性能。
- b.N 会自动增长,确保函数执行足够多次,以消除偶然的波动。
1 | PS E:\code2024\go-practice> go test -bench . |
如果在运行前基准测试需要一些耗时的配置,则可以使用 b.ResetTimer() 先重置定时器,例如:
1 | func BenchmarkHello(b *testing.B) { |