学习参考来源: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. Windows Terminal 终端 SSH连接centos7 linux

    1.在Windows Store中安装 Windows Terminal 2.打开Windows Terminal,使用下拉箭头,打开设置. 3.在左侧点击"添加新配置文件",再点 ...

  2. CF424A Squats 题解

    Content 给定一个长度为 \(n\) 的仅由 x 和 X 组成的字符串,求使得字符串中 x 和 X 的数量相等需要修改的次数,并输出修改后的字符串. 数据范围:\(2\leqslant n\le ...

  3. java 编程基础:注解的功能和作用,自定义注解

    1,什么是注解: 从JDK5开始,Java增加了对元数据 (MetaData)的支持,也就是Annotation注解,这种注解与注释不一样,注解其实是代码里的特殊标记,这些标记可以在编译.类加载 运行 ...

  4. call this的范围

    var f1=function(){this.a="类f1的实例的a属性"}; f1代表一个类: f1.a='对象f1的a属性'; var f2=function(){};//类f ...

  5. qt5之使用QtXlsxWriter库

    note Qt version: 5.12 platform: os x 10.15 本文将介绍直接使用QtXlsxWriter源码 准备 下载QtXlsxWriter 使用Qt Creator 创建 ...

  6. c++指针常量和常量指针概述

    个人理解,欢迎指正 这个简单,简单,简单(不要有心里压力:认为很难) 本文将会解决:  A.变与不变 B.判断指针常量和常量指针. C.常量指针指针常量.本文不涉及. 1.概述 A.指针: 说到底,还 ...

  7. 【九度OJ】题目1180:对称矩阵 解题报告

    [九度OJ]题目1180:对称矩阵 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1180 题目描述: 输入一个N维矩阵,判断是否对称 ...

  8. codeforce 597C-Subsequences(dp+树状数组)

    题目和南阳那道题一样链接http://www.cnblogs.com/zzuli2sjy/p/4943774.html 代码: 1 #include<stdio.h> 2 #include ...

  9. DeepFool: a simple and accurate method to fool deep neural networks

    目录 概 主要内容 二分类模型 为线性 为一般二分类 多分类问题 仿射 为一般多分类 Moosavidezfooli S, Fawzi A, Frossard P, et al. DeepFool: ...

  10. [决策树]西瓜数据graphviz可视化实现

    [决策树]西瓜数据graphviz可视化实现 一.问题描述: 使用西瓜数据集构建决策树,并将构建的决策树进行可视化操作. 二.问题简析: 首先我们简单的介绍一下什么是决策树.决策树是广泛用于分类和回归 ...