Golang - 测试
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
- 测试文件的命名
- 单元测试
- 子测试集
- 子测试组
- 性能测试
- 简单测试
- 并发性能
- 性能对比
- 设置 cpu 数量, 清除耗时
- 测试前后加操作
- 全局前后增加操作 - TestMain
- 单一用例前后增加操作 - setup 和 teardown
- 运行测试
- 单元测试
- 基准测试
- 练习 - 回文判断
下面展示的所有示例中代码都在这里
测试文件的命名
- xxx_test.go
- 比如测 split.go 文件, 命名就为 split_test.go
单元测试
子测试集
package split // 导入本包
import (
"reflect"
"testing"
)
// 命名最好以 Test 开头, 参数必须是这个
func TestSplit(t *testing.T) {
get := Split("a:b:c", ":") // 我们做测试的请求
want := []string{"a", "b", "c"} // 期望得到的结果
if ok := reflect.DeepEqual(get, want); !ok { // 这是一个深度对比, 可以对比引用类型
t.Fatalf("期望结果: %v, 实际结果: %v\n", want, get)
}
}
func TestNoneSplit(t *testing.T) {
get := Split("a:b:c", "|")
want := []string{"a:b:c"}
if ok := reflect.DeepEqual(get, want); !ok {
t.Fatalf("期望结果: %v, 实际结果: %v\n", want, get)
}
}
// 分隔符是多个字符
func TestMulitiSplit(t *testing.T) {
get := Split("abcabcabca", "bc")
want := []string{"a", "a", "a", "a"}
if ok := reflect.DeepEqual(get, want); !ok {
t.Fatalf("期望结果: %v, 实际结果: %v\n", want, get)
}
}
子测试组
package split
import (
"reflect"
"testing"
)
// 测试组
func TestGroupSplit(t *testing.T) {
// 存放测试数据的结构体
type test struct {
str string
sep string
want []string
}
// 实例化各个测试用例
tests := map[string]test{
"normal": test{
str: "a:b:c", // 测试的字符串
sep: ":", // 测试的分隔符
want: []string{"a", "b", "c"}, // 想要得到的结果
},
"none": test{
str: "a:b:c",
sep: "-",
want: []string{"a:b:c"},
},
"multi": test{
str: "a::b::c",
sep: "::",
want: []string{"a", "b", "c"},
},
"multi2": test{
str: "1231",
sep: "1",
want: []string{"", "23", ""},
},
}
// 测试
for k, v := range tests {
t.Run(k, func(t *testing.T) {
r := Split(v.str, v.sep)
if !reflect.DeepEqual(r, v.want) {
t.Errorf("excepted: %#v, get: %#v", v.want, r)
}
})
// v1.7 之前的写法
// r := Split(v.str, v.sep)
// if ok := reflect.DeepEqual(v.want, r); !ok {
// t.Fatalf("%v: 期望得到: %#v, 实际得到: %v", k, v.want, r)
// }
}
}
性能测试
简单测试
package split
// 命名由 Benchmark 开头, 参数固定
func BenchmarkSplit(b *testing.B) {
for i := 0; i < b.N; i++ {
Split("a:b:c", ":")
}
}
并发性能
package split
func BenchmarkSplitParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Split("1231", "1")
}
})
}
性能对比
package fib
import "testing"
// 当传入的数据量不同, 测试函数运算的耗时
// 这个 benchmark 是小写, 用于调用
func benchmarkFib(b *testing.B, n int) {
for i := 0; i < b.N; i++ {
Fib(n)
}
}
func BenchmarkFib2(b *testing.B) {
benchmarkFib(b, 2)
}
func BenchmarkFib20(b *testing.B) {
benchmarkFib(b, 20)
}
func BenchmarkFib100(b *testing.B) {
benchmarkFib(b, 200)
}
设置 cpu 数量, 清除耗时
// 设置 cpu 数量
b.SetParallelism(1)
// 清除上边代码的耗时
b.ResetTimer()
测试前后加操作
全局前后增加操作 - TestMain
func TestMain(m *testing.M) {
fmt.Println("start test...")
r := m.Run()
fmt.Println("end test...")
os.Exit(r)
}
单一用例前后增加操作 - setup 和 teardown
package split
func TestMultiSplit(t *testing.T) {
f := setupT(t)
defer f(t)
get := Split("a::b::c", "::")
want := []string{"a", "b", "c"}
if ok := reflect.DeepEqual(get, want); !ok {
t.Fatalf("期望结果: %v, 实际结果: %v\n", want, get)
} else {
t.Log("pass test...")
}
}
// 给单一测试用例加前后操作, 如果是性能测试, t 改 b 即可
func setupT(t *testing.T) func(t *testing.T) {
t.Log("start test...") // 测试前执行
return func(t *testing.T) { // 返回函数名, 使用时 defer 调用
t.Log("end test...")
}
}
运行测试
单元测试
go test -v # 输出详细信息
# 函数形式 子测试
go test -run=None -v # 函数名字包含 None 的测试用例
# 组形式 子测试
go test -run Split/None -v # 函数名/用例名, 如果有多个组, 需要声明函数名, 否则可以省略
# 测试覆盖率
go test -cover
# 将覆盖率输出到文件
go test -cover -coverprofile=c.out
# 将文件内容输出到浏览器
go tool cover -html=c.out
# 如果遇到如下报错说明目录下没有 go 的二进制文件, 添加即可
cover: cannot run go list: fork/exec /usr/local/go/bin/go: no such file or directory
基准测试
# -bench 指定测试的函数, . 表示所有
go test -bench=.
# 可以指定使用几个cpu, 输出内存信息等
go test -bench=Split -cpu=2 -benchmem
"""
结果展示:
goos: darwin
goarch: amd64
pkg: code.oldboy.com/split
# 使用了 4 个 cpu 测试次数 每次操作消耗时间 每次操作占用字节数 每次操作申请内存次数
BenchmarkSplit-4 3000000 420 ns/op 112 B/op 3 allocs/op
PASS
ok code.oldboy.com/split 1.710s
"""
练习 - 回文判断
- palindrome.go
package palindrome
import (
"fmt"
"unicode"
)
// 需求: 判断是否为回文字符串
// 1. 需要去除标点 unicode.IsLetter 判断
// 2. 需要忽略大小写 unicode.ToLower 转换
func isPalindrome(s string) bool {
// 用于存放"词语"
var letters []rune
// 遍历字符串
for _, l := range s {
// 判断是否为"词语", 是的话处理大小写, 添加进letters
if unicode.IsLetter(l) {
// 处理大小写
newL := unicode.ToLower(l)
// 加入切片
letters = append(letters, newL)
}
}
// 遍历判断
l := len(letters)
for i := 0; i < l/2; i++ {
if letters[i] != letters[l-i-1] {
return false
}
}
return true
}
- palindrome_test.go
package palindrome
import (
"reflect"
"testing"
)
func TestIsPalindrome(t *testing.T) {
// 测试用例结构
type test struct {
str string
want bool
}
// 测试组
tests := map[string]test{
"normal": test{
str: "abcba",
want: true,
},
"withXx": test{
str: "a:ba",
want: true,
},
// 经测试发现字节索引方式判断会导致断码, 已经优化
"chinese": test{
str: "油灯少灯油",
want: true,
},
}
// 执行用例
for name, v := range tests {
t.Run(name, func(t *testing.T) {
r := isPalindrome(v.str)
if !reflect.DeepEqual(r, v.want) {
t.Errorf("excepted: %#v, get: %#v", v.want, r)
}
})
}
}
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |