学习参考来源:https://www.liwenzhou.com/posts/Go/16_test/

go test工具

必须导入包:

import "testing"

go test命令是一个按照一定约定和组织的测试代码的驱动程序,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。

*_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。

类型 格式 作用
测试函数 函数名前缀为Test 测试程序的一些逻辑行为是否正确
基准函数 函数名前缀为Benchmark 测试函数的性能
示例函数 函数名前缀为Example 为文档提供示例文档

go test命令会遍历所有的*_test.go文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。

go test命令常用参数

  • go test
  • go test -v ./... 文件夹里还包括文件夹,可以添加./...来递归测试。
  • go test -v 添加-v参数,查看测试函数名称和运行时间;
  • go test -v ./split_test.go 指定运行某个测试文件;
    • 测试指定的_test.go文件,如果是在同一个包下,需要把测试文件和源文件都写出来;
    • go test -v ./split.go ./split_test.go
  • go test -v ./split.go ./split_test.go -test.run TestSplit 测试split_test.go里的某一个方法;

不同环境下编译go test可执行文件,编译以后可以直接使用e2e.test,

GOOS=darwin go test -c ./e2e  -o e2e.test
GOOS=linux go test -c ./e2e -o e2e.test
GOOS=windows go test -c ./e2e -o e2e.test

测试函数

基本使用

在文件名为split_test.go,格式固定为*_test.go

package split
import (
"fmt"
"reflect"
"testing"
)
func TestSplit(t *testing.T) { // 格式固定为:Test*(){}
got := Split("a:b:c", ":")
except := []string{"a", "b", "c"}
if !reflect.DeepEqual(got, except) { // 因为slice不能比较直接,借助反射包中的方法比较
fmt.Printf("excepted:%v, got:%v", except, got) // 测试失败输出错误提示
}
}

其中参数t用于报告测试失败和附加的日志信息。 testing.T的拥有的方法如下:

这里只介绍集中常用到的,链接出是全部说明:https://studygolang.com/static/pkgdoc/pkg/testing.htm#T

  • func (c *T) Error(args ...interface{}) 相当于在调用 Log 之后调用 Fail;

  • func (c *T) Errorf(format string, args ...interface{})

  • func (c *T) Fail() 将当前测试标识为失败,但是仍继续执行该测试;

  • func (c *T) FailNow() 将当前测试标识为失败并停止执行该测试,在此之后,测试过程将在下一个测试或者下一个基准测试中继续;

    FailNow 必须在运行测试函数或者基准测试函数的 goroutine 中调用,而不能在测试期间创建的 goroutine 中调用。调用 FailNow 不会导致其他 goroutine 停止。

  • func (c *T) Fatal(args ...interface{}) 调用 Fatal 相当于在调用 Log 之后调用 FailNow;

  • func (c *T) Fatalf(format string, args ...interface{})

  • func (c *T) Log(args ...interface{})

  • func (c *T) Logf(format string, args ...interface{})

测试组和子测试

func TestGroupSub(t *testing.T) {
type test struct { // 定义test结构体
input string
sep string
want []string
}
tests := map[string]test{ // 测试用例使用map存储,测试组(逻辑上)
"simple": {input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
"wrong sep": {input: "a:b:c", sep: ",", want: []string{"a:b:c"}},
"more sep": {input: "abcd", sep: "bc", want: []string{"a", "d"}},
"leading sep": {input: "我爱你", sep: "爱", want: []string{"我", "你"}},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) { //子测试
got := Split(tc.input, tc.sep)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("name:%s excepted:%#v, got:%#v", name, tc.want, got) // 将测试用例的name格式化输出
}
})
}
}

output:

--- PASS: TestGroupSub (0.00s)
--- PASS: TestGroupSub/simple (0.00s)
--- PASS: TestGroupSub/wrong_sep (0.00s)
--- PASS: TestGroupSub/more_sep (0.00s)
--- PASS: TestGroupSub/leading_sep (0.00s)
PASS

测试覆盖率

暂时不常用到,了解:https://www.liwenzhou.com/posts/Go/16_test/#autoid-2-4-0

基准测试

https://www.liwenzhou.com/posts/Go/16_test/#autoid-2-5-0

https://studygolang.com/static/pkgdoc/pkg/testing.htm#B

func BenchmarkName(b *testing.B){
// ...
}
func BenchmarkSplit(b *testing.B) {
for i := 0; i < b.N; i++ {
got := Split("a:b:c", ":")
except := []string{"a", "b", "c"}
if !reflect.DeepEqual(got, except) {
fmt.Printf("excepted:%v, got:%v", except, got)
}
}
}
$ go test -bench=Split -benchmem
goos: darwin
goarch: amd64
pkg: golearning/1125/gotest_demo/split
BenchmarkSplit-8 1489494 794 ns/op 320 B/op 12 allocs/op
PASS
ok golearning/1125/gotest_demo/split 3.003s

根据输出信息:

  • 基准测试并不会默认执行,需要增加-bench参数;
  • 基准测试添加-benchmem参数,来获得内存分配的统计数据;
  • 其中BenchmarkSplit-8表示对Split函数进行基准测试,数字8表示GOMAXPROCS的值,这个对于并发基准测试很重要。1489494794ns/op表示平均每次调用Split函数耗时794ns`;
  • 320 B/op表示每次操作内存分配了320字节,12 allocs/op则表示每次操作进行了12次内存分配。
  • 在执行中会根据实际的case执行时间是否是稳定的,会一直增加b.N的次数以达到执行时间是一种稳定的状态.比如说第一次我们执行10nm,第二次100nm,第三次300nm,那他就不会停止,会一直增加,等到平均每次执行的时间趋近于稳定他才会停;

内存分配次数非常占用时间,所以需要进行优化,减少内存分配:

func Split(s, sep string) (result []string) {
result = make([]string, 0, strings.Count(s, sep)+1) // strings.Count(s, sep)how many sep in s
i := strings.Index(s, sep) // 找到sep所在的下标
for i > -1 { // strings.Index(s, sep), 当s中没有sep时,return -1;
result = append(result, s[:i]) //
s = s[i+len(sep):] // 如果是中文的话就不是+1,要加上一个中文字符的长度;
i = strings.Index(s, sep) //
}
result = append(result, s)
return
}
goos: darwin
goarch: amd64
pkg: golearning/1125/gotest_demo/split
BenchmarkSplit-8 1894738 641 ns/op 256 B/op 10 allocs/op
PASS
ok golearning/1125/gotest_demo/split 2.581s

TestMain

一个包下面只能有一个TestMain方法。这个方法就和main()方法差不太多。他会在其他的Test方法之前执行,我们可以利用他做一些初始化数据操作,执行完后释放资源等操作。;

*_test.go文件中定义TestMain函数;

func TestMain(m *testing.M) {
if os.Getenv("TEST_ENV") == "" {
fmt.Println("env is not configured")
return
}
setup() //自定义的 setup something
code := m.Run() //运行测试的主程序
tearDown() //自定义的,有时候测试额外添加了一些东西,通过这个让测试前后几乎无影响;
os.Exit(code)
}

一旦使用了TestMain(m *testing.M),不管怎么测试都是通过这个类似main函数顺序执行的;也就是先setup,m.Run()执行测试,tearDown()取消设置;

就算是只运行某一个测试函数,也是安装这个过程进行的;

$ TEST_ENV=aaa go test -v ./split.go ./split_test.go -test.run TestSplit
setup something
=== RUN TestSplit
--- PASS: TestSplit (0.00s)
PASS
tear down something
ok command-line-arguments 0.386s

Go语言测试:testing的更多相关文章

  1. [译] Go语言测试进阶版建议与技巧

    阅读本篇文章前,你最好已经知道如何写基本的单元测试.本篇文章共包含3个小建议,以及7个小技巧. 建议一,不要使用框架 Go语言自身已经有一个非常棒的测试框架,它允许你使用Go编写测试代码,不需要再额外 ...

  2. GO语言测试

    Go语言的测试技术是相对低级的.它依赖一个 go test 测试命令和一组按照约定方式编写的 测试函数,测试命令可以运行这些测试函数.编写相对轻量级的纯测试代码是有效的,而且它很容易延伸到基准测试和示 ...

  3. HoloLens开发手记 - 测试 Testing

    测试HoloLens应用的做法和测试Windows应用很类似.所有常规的内容都应该被考虑在内(功能.互操作性.性能.安全性.可靠性等等),然而有些特性是HoloLens特有的,在PC或者手机上无法测试 ...

  4. C语言--测试电脑存储模式(大端存储OR小端存储)

    相信大家都知道大端存储和小端存储的概念,这在平时,我们一般不用考虑,但是,在某些场合,这些概念就显得很重要,比如,在 Socket 通信时,我们的电脑是小端存储模式,可是传送数据或者消息给对方电脑时, ...

  5. Go语言测试代码

    第一次学go语言,测试代码 package main import "fmt" var age int; const sex = 0 func init() { fmt.Print ...

  6. c语言测试芯片好坏

    问题描述有n个(2<n<20)芯片,好的或坏的,并且有比坏的芯片更多的已知的好的芯片.每个芯片都可以用来测试其他芯片.当用一个好的芯片测试其他芯片时,它可以正确地给出被测芯片是好是坏.当用 ...

  7. 一套很有意思的C语言测试题目

    网络上逛博客,发现了一套很有意思的测试题目: https://kobes.ca/ 大家有兴趣可以做一下,考一些关于C语言使用的细节: 中文翻译参考: https://www.cnblogs.com/l ...

  8. 脚本语言&& Performance Testing

    watin: http://www.cnblogs.com/dahuzizyd/archive/2007/04/13/ruby_on_rails_windows_instatnrails_study_ ...

  9. 51单片机连接24C02-C语言测试代码

    忙了一天多终于透彻了,自己写的不好使,用别人的逐步分析改成自己的,我写得非常简洁易懂. 我总结3点需要注意的地方 1.关闭非IIC通信器件,比如我的开发板SDA和SCL也连接了DS1302,造成干扰会 ...

随机推荐

  1. CF31B Sysadmin Bob 题解

    Content 给定一个字符串 \(s\),请将其分解为诸如 \(\texttt{xx@xx}\) 的子串,并将分解后的所有子串输出,或者说不存在这样的方案. 数据范围:\(1\leqslant|s| ...

  2. .NET Core基础篇之:白话管道中间件

    在.Net Core中,管道往往伴随着请求一起出现.客户端发起Http请求,服务端去响应这个请求,之间的过程都在管道内进行. 举一个生活中比较常见的例子:旅游景区. 我们都知道,有些景区大门离景区很远 ...

  3. 简单聊聊mysql的脏读、不可重复读

    最近,在一次 mysql 死锁的生产事故中,我发现,关于 mysql 的锁.事务等等,我所知道的东西太碎了,所以,我试着用几个例子将它们串起来.具体做法就是通过不断地问问题.回答问题,再加上" ...

  4. windows10使用vscode+cmake编译c++代码

    概述 本文将介绍 VScode + cmake 在 windows10上编译c++代码 前提: 我之前已经安装过VS2017, 故 编译将采用cl.exe. 开始之前 本文演示环境基于 windows ...

  5. 平衡二叉树判定方法(c++)实现

    !!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist -- 欢迎指正-- 平衡二叉树特点: 任意一个结点的平衡因子(左子树高度 - 右子树高度)的 ...

  6. C/C++ 结构体字节对齐

    在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题.从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但是事实上不是如此 ...

  7. 【LeetCode】面试题62. 圆圈中最后剩下的数字 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 约瑟夫环 日期 题目地址:https://leetco ...

  8. 【九度OJ】题目1434:今年暑假不AC 解题报告

    [九度OJ]题目1434:今年暑假不AC 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1434 题目描述: "今年暑假不A ...

  9. 【LeetCode】865. Smallest Subtree with all the Deepest Nodes 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  10. B. Petya and Exam

    B. Petya and Exam 题目链接 题意 给你一串字符,在这个串中所有出现的字符都是\(good\)字符,未出现的都是\(bad\)字符, 然后给你另一串字符,这个字符串中有两个特殊的字符, ...