规范
- 测试文件已_test.go结尾
- 单元测试:func TestXxx(*testing.T) 基准测试:func BenchmarkXxx
- 如果有初始化逻辑,放到TestMain()中
单元测试
运行测试指令
go test [flag] [packages]
可用assert包进行声明以检测结果的正确性
go get github.com/stretchr/testify/assert
评估测试效果——Coverage
代码覆盖率是指测试用例是否覆盖了被测试代码的全部语句、分支、函数、行数等。通过评估代码覆盖率,可以判断测试用例是否充分覆盖了被测试代码,进而评估测试效果。在Golang中,可以使用go test命令的--cover标志来计算代码覆盖率。
原理是啥?
在网上搜索了相关问题,这里做一个总结:
在 Golang 中,代码覆盖率的计算是通过在编译时插入特殊的指令来实现的,这些指令在程序运行时会被触发,从而统计出代码的覆盖率。
具体来说,编译器会在每个基本块的开头插入一个指令,这个指令会将基本块的信息(如起始位置和长度)写入一个表格中。程序运行时,会记录每个基本块是否被执行过,最后根据表格中的信息计算出代码的覆盖率。因此,在编写测试用例时,需要尽可能地覆盖被测试代码的所有分支和语句,以保证测试的充分性和准确性。
一般来说,50%~60%是正常需要的覆盖度。较好的是80%+
Mock
Mock是一种模拟测试方式,它通过模拟依赖项的行为来使测试更加可控和可靠。在Golang中,常用的Mock框架包括gomock和testify/mock和nouk/monkey。Mock可以用于模拟外部服务,或者用于模拟复杂的对象,在测试中有效地隔离被测试对象的依赖项,从而更好地进行单元测试。
这里讲一下bouk/monkey。
使用bouk/monkey进行模拟测试
bouk/monkey允许我们在运行时替换任何函数或者变量,这样我们就可以从依赖项中隔离地测试代码。
安装:
go get -u github.com/bouk/monkey
安装完成后,导入包后,就可以在测试中调用patch函数**(课程里说的打桩)**,并传递要替换的函数或变量以及替换函数或变量。下面我写了一个示例:
func TestMyFunction(t *testing.T) {
// 用始终返回相同时间的函数替换time.Now
patch := monkey.Patch(time.Now, func() time.Time {
return time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC)
})
defer patch.Unpatch()
// 现在,调用time.Now将始终返回相同的时间
result := MyFunction()
expected := "2022-01-01T00:00:00Z"
if result != expected {
t.Errorf("期望%s,但得到%s", expected, result)
}
}
在此示例中,我们测试了MyFunction,函数调用time.Now。通过使用bouk/monkey将time.Now替换为始终返回相同时间的函数,我们可以在隔离的情况下测试MyFunction,而不必担心当前时间的更改会影响结果。
应在测试完成后调用Unpatch以恢复原始函数或变量!可用defer。
课程中提到了Patch和UnPatch的原理:
本质上是通过reflect包来替换内存地址,很容易理解。
基准测试 benchmark
基准测试是一种评估代码性能的方式,用于衡量某个函数或算法的运行时间。基准测试有助于优化代码性能,特别是在涉及计算密集型操作或需要优化的代码块中。
用得上的方法:
b.ResetTimer()
指令:
go test -bench=.
例子:
// sum.go
package main
func Sum(numbers []int) int {
sum := 0
for _, num := range numbers {
sum += num
}
return sum
}
// sum_test.go
package main
import (
"testing"
)
// 基准测试函数
func BenchmarkSum(b *testing.B) {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for i := 0; i < b.N; i++ {
Sum(numbers)
}
}