Go 语言快速入门指南:Go 测试
介绍
编程不易,好的程序员也不敢保证程序无 Bug。因此,软件开发过程的一个重要部分是测试。为我们的代码编写测试是确保质量和提高可靠性的好方法。
Go 提供了 testing 包,可以用来为代码编写自动化测试。
命令可以运行这些测试的代码。
单元测试是编写有原则的 Go程序的一个重要部分。测试包提供了我们编写单元测试所需的工具,命令可以运行测试。为了便于演示,这段代码是在包 中,但它可以是任何包。测试代码通常与对应所需要测试的代码放在在同一个包里。
测试代码结构
Go 语言推荐测试文件和源代码文件放在一块,测试文件以 结尾。比如,当前 package 有 一个文件,我们想测试 中的 函数,那么应该新建 作为测试文件。
example/
|--average.go
|--average_test.go
工具会帮我们查找以该后缀命名的文件。
一个测试文件应该有的样子:
package average
import "testing" // 导入标准库的“tesing” 包
func TestAverage(t *testing.T) { // 将一个指向 testing.T 值的指针传递给函数
// 调用 testing.T 上的方法来表示测试失败
t.Error("wrong anwser")
}
测试文件由普通的 Go 函数组成,但需要遵循一定得约定才能使用 命令:
无需把测试代码与正在测试的代码放在同一个包中,但是如果你想从包中访问未导出的类型或函数,则最好这样做。
测试需要使用 testing 包中的类型,所以需要在每个测试文件中的 块中导入这个包。
测试函数名应该以 Test 开头。(名字的其余部分可以是你想要的任何内容,但它应该以大写字母开头)
测试函数应该接受单个参数:一个指向 值的指针。
可以对 值调用方法()来报告测试失败,但更多的选择是通过 接受一个字符串,可以帮忙解释测试失败的信息。 接受一个带格式化动词的字符串,就像 和 函数一样,可以使用 在测试的失败消息中包括其他信息,例如传递给函数的参数、得到的返回值和期望的值。
翻译一段测试的代码
我们将测试计算整数最小值的简单实现。
按照上文中测试代码的结构:
package main
import (
"fmt"
"testing"
)
func IntMin(a, b int) int {
if a < b {
return a
}
return b
}
func TestIntMinBasic(t *testing.T) {
ans := IntMin(2, -2)
if ans != -2 {
t.Errorf("IntMin(2, -2) = %d; want -2", ans)
}
}
func TestIntMinTableDriven(t *testing.T) {
var tests = []struct {
a, b int
want int
}{
{0, 1, 0},
{1, 0, 0},
{2, -2, -2},
{0, -1, -1},
{-1, 0, -1},
}
for _, tt := range tests {
testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
t.Run(testname, func(t *testing.T) {
ans := IntMin(tt.a, tt.b)
if ans != tt.want {
t.Errorf("got %d, want %d", ans, tt.want)
}
})
}
}
func BenchmarkIntMin(b *testing.B) {
for i := 0; i < b.N; i++ {
IntMin(1, 2)
}
}
通过编写一个名称以 Test 开头的函数来创建一个测试。
将报告测试失败但继续执行测试。 将报告测试失败并立即停止测试。
编写测试可能是重复性的,所以使用表格驱动的风格是习惯性的,测试输入和预期输出被列在一个表格中,一个单一的循环走过它们并执行测试逻辑。
可以运行 "子测试",每个表项都有一个。当执行 时,这些子测试会分别显示出来。
以粗略模式运行当前项目中的所有测试.
运行结果:
$ go test -v
=== RUN TestIntMinBasic
--- PASS: TestIntMinBasic (0.00s)
=== RUN TestIntMinTableDriven
=== RUN TestIntMinTableDriven/0,1
=== RUN TestIntMinTableDriven/1,0
=== RUN TestIntMinTableDriven/2,-2
=== RUN TestIntMinTableDriven/0,-1
=== RUN TestIntMinTableDriven/-1,0
--- PASS: TestIntMinTableDriven (0.00s)
--- PASS: TestIntMinTableDriven/0,1 (0.00s)
--- PASS: TestIntMinTableDriven/1,0 (0.00s)
--- PASS: TestIntMinTableDriven/2,-2 (0.00s)
--- PASS: TestIntMinTableDriven/0,-1 (0.00s)
--- PASS: TestIntMinTableDriven/-1,0 (0.00s)
PASS
ok main.go 0.005s
以上代码翻译自:https://gobyexample.com/testing
一个简单的"average"函数测试
比如,我们的计算平均分的代码 可以这样写:
package average
import "testing"
func GetAverage(scores []float64) float64 {
sum := float64(0)
for _, value := range scores {
sum += value
}
return sum / float64((len(scores)))
}
func TestAverage(t *testing.T) {
var v float64
v = GetAverage([]float64{1, 2, 3, 4, 5})
if v != 3 {
t.Error("Expected 3, got ", v)
}
}
测试结果:
$ go test -v
=== RUN TestAverage
--- PASS: TestAverage (0.00s)
PASS
ok main.go/Go_test_demo 0.005s
表驱动开发
上面的 测试只能是单个测试数据,如果我们想要测试多个不同的数据,就可以使用到表驱动开发。
不需要单独维护测试函数,可以构建一个由输入数据和期望的输出构成的表,然后使用单个测试函数检查表中的每一项。
可以使用一个 testpair 的结构体来分别保存值 切片和 结果,如:
type testpair struct {
values []float64
average float64
}
var tests = []testpair{
{[]float64{1, 2}, 1.5},
{[]float64{1, 1, 1, 1, 1, 1}, 1},
{[]float64{-1, 1}, 0},
{[]float64{1, 1.4}, 1.2},
}
然后循环遍历每个 tests 的值,将这些值传递给 函数:
package average
import "testing"
type testpair struct {
values []float64
average float64
}
var tests = []testpair{
{[]float64{1, 2}, 1.5},
{[]float64{1, 1, 1, 1, 1, 1}, 1},
{[]float64{-1, 1}, 0},
{[]float64{1, 1.4}, 1.2},
}
func GetAverage(scores []float64) float64 {
sum := float64(0)
for _, value := range scores {
sum += value
}
return sum / float64((len(scores)))
}
func TestAverage(t *testing.T) {
for _, pair := range tests {
v := GetAverage(pair.values)
if v != pair.average {
t.Error(
"For", pair.values,
"expected", pair.average,
"got", v,
)
}
}
}
运行上诉代码,得:
$ go test -v
=== RUN TestAverage
--- PASS: TestAverage (0.00s)
PASS
ok main.go/Go_test_demo 0.005s
测试驱动开发
我们经常能听到测试驱动开发(Test-Driven-Development,TDD)的模式:
总结
Go 包含 包,可以用来为代码编写自动化测试
命令用来运行这些测试,这条命令只针对于以 结尾的文件。
自动化测试使用一组特定的输入来运行代码,并寻找特定的结果。如果代码输入与期望值匹配,则测试通过,否则测试失败并报错。
测试函数必须接受单个参数:一个指向 值得指针
表驱动测试是处理输入和预期输出的“表”的测试,通过传入一组输入,测试这一组输出,检查代码的输出是否与预期值匹配。这样可以减少代码的重复率。
参考书籍:
《Head First Go》
https://gobyexample.com/testing