Golang 语言的单元测试和性能测试(也叫 压力测试)
Golang单元测试对文件名和方法名,参数都有很严格的要求。
例如:
1、文件名必须以xx_test.go命名
2、方法必须是Test[^a-z]开头(T必须大写),func TestXxx (t *testing.T)
,Xxx
部分可以为任意的字母数字的组合,但是首字母不能是小写字母[a-z],例如Testintdiv
是错误的函数名。
3、方法参数必须 t *testing.T
4、测试用例会按照源代码中写的顺序依次执行
5、函数中通过调用testing.T
的Error
, Errorf
, FailNow
, Fatal
, FatalIf
方法,说明测试不通过,调用Log
方法用来记录测试的信息。
test的运行方式:
其中后面的test为test目录
Go 提供了 TestMain(*testing.M)
的函数,它在需要的时候代替运行所有的测试。使用 TestMain() 函数时,您有机会在测试运行之前或之后插入所需的任何自定义代码,但唯一需要注意的是必须处理 flag 解析并使用测试结果调用 os.Exit()。这听起来可能很复杂,但实际上只有两行代码。
测试的覆盖率
go tool命令可以报告测试覆盖率统计。使用 go test -cover 测试覆盖率。
可视化查看覆盖率:
执行go tool cover -html=cover.out命令,会在/tmp目录下生成目录coverxxxxxxx,比如/tmp/cover404256298。目录下有一个 coverage.html文件。用浏览器打开coverage.html,即可以可视化的查看代码的测试覆盖情况。
从内部测试
golang中大多数测试代码都是被测试包的源码的一部分。这意味着测试代码可以访问包种未导出的符号以及内部逻辑。就像我们之前看到的那样。
注:比如$GOROOT/src/pkg/path/path_test.go与path.go都在path这个包下。
从外部测试
有些时候,你需要从被测包的外部对被测包进行测试,比如测试代码在package foo_test下,而不是在package foo下。这样可以打破依赖循环。
1、功能测试
上一个完整的例子:
// how to run: go test -v github.com/welhzh/dago/test
package client import (
"flag"
"fmt"
"os"
"testing" "github.com/welhzh/dago/client"
) func initTest() {
} func destroyTest() {
}
// test example
func TestSum(t *testing.T) {
// 任选一种测试方式进行测试
// ================= test 方式一 ====================
var testInputs = []struct {
slice []int
// other inputs
expect int
}{
{[]int{, , , , }, },
{[]int{, , , , -}, },
} for _, oneInput := range testInputs {
actual := oneInput.slice[]
if actual != oneInput.expect {
t.Errorf("Sum(%q, %q) = %v; want %v", oneInput, oneInput, actual, oneInput.expect)
}
}
// ================================================= // ================= test 方式二 ====================
t.Run("[1,2,3,4,5]", testSumFunc(testInputs[].slice, testInputs[].expect))
t.Run("[1,2,3,4,-5]", testSumFunc(testInputs[].slice, testInputs[].expect))
// =================================================
} func testSumFunc(numbers []int, expected int) func(*testing.T) {
return func(t *testing.T) {
actual := numbers[]
if actual != expected {
t.Error(fmt.Sprintf("Expected the sum of %v to be %d but instead got %d!", numbers, expected, actual))
}
}
} func TestMain(m *testing.M) {
fmt.Println("test begin ...")
initTest() flag.Parse()
exitCode := m.Run() destroyTest()
fmt.Println("test end ...")
// Exit
os.Exit(exitCode)
}
格式形如:
go test [-c] [-i] [build flags] [packages] [flags for test binary]
参数解读:
-c : 编译go test成为可执行的二进制文件,但是不运行测试。
-i : 安装测试包依赖的package,但是不运行测试。
关于build flags,调用go help build,这些是编译运行过程中需要使用到的参数,一般设置为空
关于packages,调用go help packages,这些是关于包的管理,一般设置为空
关于flags for test binary,调用go help testflag,这些是go test过程中经常使用到的参数
-test.v : 是否输出全部的单元测试用例(不管成功或者失败),默认没有加上,所以只输出失败的单元测试用例。
-test.run pattern: 只跑哪些单元测试用例
-test.bench patten: 只跑那些性能测试用例
-test.benchmem : 是否在性能测试的时候输出内存情况
-test.benchtime t : 性能测试运行的时间,默认是1s
-test.cpuprofile cpu.out : 是否输出cpu性能分析文件
-test.memprofile mem.out : 是否输出内存性能分析文件
-test.blockprofile block.out : 是否输出内部goroutine阻塞的性能分析文件
-test.memprofilerate n : 内存性能分析的时候有一个分配了多少的时候才打点记录的问题。这个参数就是设置打点的内存分配间隔,也就是profile中一个sample代表的内存大小。默认是设置为512 * 1024的。如果你将它设置为1,则每分配一个内存块就会在profile中有个打点,那么生成的profile的sample就会非常多。如果你设置为0,那就是不做打点了。
你可以通过设置memprofilerate=1和GOGC=off来关闭内存回收,并且对每个内存块的分配进行观察。
-test.blockprofilerate n: 基本同上,控制的是goroutine阻塞时候打点的纳秒数。默认不设置就相当于-test.blockprofilerate=1,每一纳秒都打点记录一下
-test.parallel n : 性能测试的程序并行cpu数,默认等于GOMAXPROCS。
-test.timeout t : 如果测试用例运行时间超过t,则抛出panic
-test.cpu 1,2,4 : 程序运行在哪些CPU上面,使用二进制的1所在位代表,和nginx的nginx_worker_cpu_affinity是一个道理
-test.short : 将那些运行时间较长的测试用例运行时间缩短
2、性能测试
如果需要进行性能测试,则函数开头使用Benchmark就可以了。
//性能测试
func BenchmarkFibonacci(b *testing.B) {
for i := ; i < b.N; i++ {
Fibonacci()
}
}
接下来执行这个性能测试:
$ go test -bench=. lib
PASS
BenchmarkFibonacci 5000000 436 ns/op
ok lib 2.608s
其中第二行输出表示这个函数运行了5000000次,平均运行一次的时间是436ns。
这个性能测试只测试参数为10的情况。如果有需要可以测试多个参数:
//测试参数为5的性能
func BenchmarkFibonacci5(b *testing.B) {
for i := ; i < b.N; i++ {
Fibonacci()
}
} //测试参数为20的性能
func BenchmarkFibonacci20(b *testing.B) {
for i := ; i < b.N; i++ {
Fibonacci()
}
}
运行一下:
$ go test -bench=. lib
PASS
BenchmarkFibonacci 5000000 357 ns/op
BenchmarkFibonacci5 100000000 29.5 ns/op
BenchmarkFibonacci20 50000 44688 ns/op
ok lib 7.824s
如果性能测试的方法非常多,那需要的时间就会比较久。可以通过-bench=参数设置需要运行的性能测试的函数:
$ go test -bench=Fibonacci20 lib
PASS
BenchmarkFibonacci20 50000 44367 ns/op
ok lib 2.677s
3、IO相关测试 (高级测试技术)
testing/iotest包中实现了常用的出错的Reader和Writer,可供我们在io相关的测试中使用。主要有:
触发数据错误dataErrReader,通过DataErrReader()
函数创建
读取一半内容的halfReader,通过HalfReader()
函数创建
读取一个byte的oneByteReader,通过OneByteReader()
函数创建
触发超时错误的timeoutReader,通过TimeoutReader()
函数创建
写入指定位数内容后停止的truncateWriter,通过TruncateWriter()
函数创建
读取时记录日志的readLogger,通过NewReadLogger()
函数创建
写入时记录日志的writeLogger,通过NewWriteLogger()
函数创建
4、黑盒测试 (高级测试技术)
testing/quick包实现了帮助黑盒测试的实用函数 Check和CheckEqual。
Check函数的第1个参数是要测试的只返回bool值的黑盒函数f,Check会为f的每个参数设置任意值并多次调用,如果f返回false,Check函数会返回错误值 *CheckError。Check函数的第2个参数 可以指定一个quick.Config类型的config,传nil则会默认使用quick.defaultConfig。quick.Config结构体包含了测试运行的选项。
# /usr/local/go/src/math/big/int_test.go
func checkMul(a, b []byte) bool {
var x, y, z1 Int
x.SetBytes(a)
y.SetBytes(b)
z1.Mul(&x, &y) var z2 Int
z2.SetBytes(mulBytes(a, b)) return z1.Cmp(&z2) ==
} func TestMul(t *testing.T) {
if err := quick.Check(checkMul, nil); err != nil {
t.Error(err)
}
}
CheckEqual函数是比较给定的两个黑盒函数是否相等,函数原型如下:
func CheckEqual(f, g interface{}, config *Config) (err error)
5、http测试 (高级测试技术)
net/http/httptest包提供了HTTP相关代码的工具,我们的测试代码中可以创建一个临时的httptest.Server来测试发送HTTP请求的代码:
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, client")
}))
defer ts.Close() res, err := http.Get(ts.URL)
if err != nil {
log.Fatal(err)
} greeting, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
} fmt.Printf("%s", greeting)
还可以创建一个应答的记录器httptest.ResponseRecorder
来检测应答的内容:
handler := func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "something failed", http.StatusInternalServerError)
} req, err := http.NewRequest("GET", "http://example.com/foo", nil)
if err != nil {
log.Fatal(err)
} w := httptest.NewRecorder()
handler(w, req) fmt.Printf("%d - %s", w.Code, w.Body.String())
6、在进程里测试 (高级测试技术)
当我们被测函数有操作进程的行为,可以将被测程序作为一个子进程执行测试。下面是一个例子:
//被测试的进程退出函数
func Crasher() {
fmt.Println("Going down in flames!")
os.Exit()
} //测试进程退出函数的测试函数
func TestCrasher(t *testing.T) {
if os.Getenv("BE_CRASHER") == "" {
Crasher()
return
}
cmd := exec.Command(os.Args[], "-test.run=TestCrasher")
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
err := cmd.Run()
if e, ok := err.(*exec.ExitError); ok && !e.Success() {
return
}
t.Fatalf(
7、竞争检测 (高级测试技术)
当两个goroutine并发访问同一个变量,且至少一个goroutine对变量进行写操作时,就会发生数据竞争(data race)。
为了协助诊断这种bug,Go提供了一个内置的数据竞争检测工具。
通过传入-race选项,go tool就可以启动竞争检测。
$ go test -race mypkg // to test the package
$ go run -race mysrc.go // to run the source file
$ go build -race mycmd // to build the command
$ go install -race mypkg // to install the package
一个数据竞争检测的例子:
//testrace.go package main import “fmt”
import “time” func main() { var i int =
go func() {
for {
i++
fmt.Println("subroutine: i = ", i)
time.Sleep( * time.Second)
}
}() for {
i++
fmt.Println("mainroutine: i = ", i)
time.Sleep( * time.Second)
}
}
运行:
$ go run -race testrace.go
8、并发测试 (高级测试技术)testing with concurrency
当测试并发代码时,总会有一种使用sleep的冲动。大多时间里,使用sleep既简单又有效。
但大多数时间不是”总是“。
我们可以使用Go的并发原语让那些奇怪不靠谱的sleep驱动的测试更加值得信赖。
9、静态分析工具vet查找错误 (高级测试技术)
vet工具用于检测代码中程序员犯的常见错误:
– 错误的printf格式
– 错误的构建tag
– 在闭包中使用错误的range循环变量
– 无用的赋值操作
– 无法到达的代码
– 错误使用mutex
等等。
使用方法:
go vet [package]
10、Mocks和fakes (高级测试技术)
通过在代码中使用interface,Go可以避免使用mock和fake测试机制。
例如,如果你正在编写一个文件格式解析器,不要这样设计函数:
func Parser(f *os.File) error
作为替代,你可以编写一个接受interface类型的函数:
func Parser(r io.Reader) error
和bytes.Buffer、strings.Reader一样,*os.File也实现了io.Reader接口。
Golang 语言的单元测试和性能测试(也叫 压力测试)的更多相关文章
- [转贴]LTP--linux稳定性测试 linux性能测试 ltp压力测试 ---IBM 的 linux test project
https://blog.csdn.net/melody157398/article/details/24354415 LTP--linux稳定性测试 linux性能测试 ltp压力测试 ---I ...
- LTP--linux稳定性测试 linux性能测试 ltp压力测试 ---IBM 的 linux test project
LTP--linux稳定性测试 linux性能测试 ltp压力测试 ---IBM 的 linux test project Peter盼 2014-04-23 11:25:49 20302 收藏 ...
- LTP--linux稳定性测试 linux性能测试 ltp压力测试 内核更新 稳定性测试
LTP--linux稳定性测试 linux性能测试 ltp压力测试 zhangzj1030关注14人评论33721人阅读2011-12-09 12:07:45 说明:在写这篇文章之前,本人也不曾了 ...
- LTP--linux稳定性测试 linux性能测试 ltp压力测试 ltp-pan
LTP--linux稳定性测试 linux性能测试 ltp压力测试 zhangzj1030关注14人评论33710人阅读2011-12-09 12:07:45 说明:在写这篇文章之前,本人也不曾了 ...
- 云server 性能测试web压力测试
前言:如今,云server主流.它已成为许多中小型企业的首选server,但是云server它是一个虚拟机.所以性能是一个大问题,从这里开始介绍云server性能测试,云webserver压力测试. ...
- Jmeter性能测试-分布式压力测试
作为一个测试行业的菜鸟,由于投身于一个小公司,包揽所有的测试.刚开始的功能测试到接口测试,稳定性测试,兼容性测试等,一般由于是小项目所以对于性能有所忽略,也没怎么涉及,公司接了个大项目,后期对于性能上 ...
- 一款C++写的tcp性能测试(压力测试)工具
基线终于开发完了,自己写的服务器性能和压力承受能力到底怎么样,在网上没有找到合适的测试工具可以用.于是自己基于MFC写了一款测试工具. 启动界面如下: 设置参数后,运行如下: 运行完界面如下: 源码路 ...
- Web性能测试篇:AB 压力测试
1. 压力测试的概念\定义 1.这段话是给刚接触\学习性能测试知识的初学者,在实际工作中都会接触到性能测试.压力测试.负载测试等专业名词也容易混淆,下面带大家熟悉下这到底是怎么定义: 1.1.性能测试 ...
- 如何使用VSTS做压力测试
1 前言 1.1 目的 本文档主要介绍如何在VSTS环境中进行LoadTest测试,给测试人员和初次使用者提供参考. 对该工具进行LoadTest测试的优劣进行简单的分析说明. 1.2 软件版本 本文 ...
随机推荐
- 【Richard 的刷(水)题记录】
大概想了想,还是有个记录比较好. 9/24 网络流一日游: 最大流:bzoj1711[Usaco2007 Open]Dining 拆点 BZOJ 3993 Sdoi2015 星际战争 二分 P.S.这 ...
- 【Dp】Bzoj1296 [SCOI2009] 粉刷匠
Description windy有 N 条木板需要被粉刷. 每条木板被分为 M 个格子. 每个格子要被刷成红色或蓝色. windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色. 每个 ...
- Github泄露扫描系统
Github leaked patrol Github leaked patrol为一款github泄露巡航工具: 提供了WEB管理端,后台数据库支持SQLITE3.MYSQL和POSTGRES 双引 ...
- EffictiveC++笔记 第3章
Chapter 3 资源管理 条款13: 以对象管理资源 有时即使你顺利地写了对应对象的delete语句,但是前面的区域可能会有一个过早的return语句或者抛出了异常.它们一旦执行,控制流绝不会触及 ...
- 对Javascript 类、原型链、继承的理解
一.序言 和其他面向对象的语言(如Java)不同,Javascript语言对类的实现和继承的实现没有标准的定义,而是将这些交给了程序员,让程序员更加灵活地(当然刚开始也更加头疼)去定义类,实现继承 ...
- JAVA线程及简单同步实现的原理解析
线程 一.内容简介: 本文主要讲述计算机中有关线程的相关内容,以及JAVA中关于线程的基础知识点,为以后的深入学习做铺垫.如果你已经是高手了,那么这篇文章并不适合你. 二.随笔正文: 1.计算机系统组 ...
- subprocess实时获取结果和捕获错误
需要调用命令行来执行某些命令,主要是用 subprocess 实时获取结果和捕获错误,发现subprocess的很多坑. subprocess 普通获取结果方式,其需要命令完全执行才能返回结果: im ...
- 残差网络(Residual Networks, ResNets)
1. 什么是残差(residual)? “残差在数理统计中是指实际观察值与估计值(拟合值)之间的差.”“如果回归模型正确的话, 我们可以将残差看作误差的观测值.” 更准确地,假设我们想要找一个 $x$ ...
- java实现开根号算法
public static void main(String[] args) { long start = System.currentTimeMillis(); double target=9876 ...
- Base64编码转换原理
Base64编码,是我们程序开发中经常使用到的编码方法.它是一种基于用64个可打印字符来表示二进制数据的表示方法.它通常用作存储.传输一些二进制数据编码方法.它其实只是定义用可打印字符传输内容一种方法 ...