学习参考来源: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. 通过idea创建Maven项目整合Spring+spring mvc+mybatis

    创建项目 File→new→project             然后就不断next直到项目面板出来 设置文件夹         注意:这里我个人习惯,在java下还建了ssm文件夹,然后再cont ...

  2. LuoguP7285 「EZEC-5」修改数组 题解

    Content 有一个长度为 \(n\) 的 \(0/1\) 串,你可以修改当中的一些元素,求修改后最长的连续为 \(1\) 的子串长度减去修改次数的最大值. 数据范围:\(1\leqslant n\ ...

  3. 『学了就忘』Linux系统定时任务 — 88、循环执行定时任务

    目录 1.crond服务管理与访问控制 2.crontab命令的访问控制 3.用户级别的crontab命令 4.crontab命令的注意事项 5.系统的crontab设置 (1)/etc/cronta ...

  4. Tornado 异步浅解

    7.1 认识异步 1. 同步 我们用两个函数来模拟两个客户端请求,并依次进行处理: #!/usr/bin/env python3 # -*- coding:utf-8 -*- # @Time: 202 ...

  5. java web 404错误页面配置

    java web 404错误页面配置:注意红框的地方,在工程的web.xml文件里的最开头加入如下的内容便可,但是也有问题,针对以.action后缀名和.jsp后缀名不起作用, 因为后面配置了一些拦截 ...

  6. libevent源码学习(6):事件处理基础——event_base的创建

    目录前言创建默认的event_baseevent_base的配置event_config结构体创建自定义event_base--event_base_new_with_config禁用(避免使用)某一 ...

  7. docker安装artemis

    Dockerfile # Licensed to the Apache Software Foundation (ASF) under one # or more contributor licens ...

  8. IDEA通过git回滚到某个提交节点或某个版本

    1.项目右键后,点击"Git - Show History" 这里会显示有历史提交的版本记录(这里我们假设要回滚到 "提交" 版本中) 2.选中 "提 ...

  9. Linux(centos)系统导出数据库文件命令

    mysqldump -uroot -p test > /test.sql -uroot 其中的root是数据库的用户名 test是要导出的数据库名字 test.sql 是要导出的数据库文件名字, ...

  10. Qt5获取可用串口

    概述 本文将介绍Qt5使用类QSerialPortInfo获取可以用串口号 效果 机器上配置的虚拟串口 程序获取结果 源码开始 .pro文件中添加下面的代码 QT += serialport 然后,执 ...