接着上一篇中的go module创建项目calc为例,在simplemath
包中,是使用在命令行中使用交互式的方式进行测试,现在可以为这几个函数实现单元测试, go test
,这个测试工具来自于 Go 官方的 gc
工具链。
运行 go test
命令将执行当前目录下的包的测试代码,它会寻找 *_test.go
文件,并在这些文件中,寻找符合 TestXxx(*testing.T){}
命名的函数和参数(即,接收 *testing.T
参数的函数,命名为 TestXxx
,Xxx
可以是任何不以小写字符开头的名字)。这个测试代码不会影响正常的编译过程,只在执行 go test
时被使用,如果对pytest
有过了解,这个就可以轻松使用
编写单元测试
接着新增add_test.go
和sqrt_test.go
两个文件,分别为add.go
和sqrt.go
编写单元测试,目录如下所示,通常测试代码和被测试代码在同一个包中
其中add_test.go
如下
package simplemath
import "testing"
func TestADD(t *testing.T) {
r := Add(1, 2)
if r != 3 {
t.Errorf("Add(1, 2) failed, Got %d, expected 3.", r)
}
}
sqrt_test.go
如下
package simplemath
import "testing"
func TestSqrt(t *testing.T) {
v := Sqrt(9)
if v != 3 {
t.Errorf("Sqrt(9) failed. Got %v, expected 3.", v)
}
}
在编写单元测试时,需要引入testing
包,类似pytest
中的pytest
包,go中的单元测试标准类型如下:
func TestXXX(t *testing.T) {
// 测试逻辑
}
运行单元测试
使用goland编译器,选择要执行的单元测试的包,如这里的simplemath
,然后痛右键菜单执行,这里和pytest几乎没有差异,因为goland和pycharm也是全家桶中的成员,所以如果之前使用过pytest和pycharm的搭配,这里就很容易上手
接着可以看到执行输出窗口如下
当然也可以在test测试文件点击如下所示,点击包名左侧的执行按钮,和上述点击工程目录栏是一样的效果
对应点击测试函数右侧的执行符号,则执行单个测试函数或者测试类。
我们知道负数无法平方根的,因此这里还要测试下sqrt函数的无效等价类测试
func TestNegativeSqrt(t *testing.T) {
v := Sqrt(-9)
if v != 3 {
t.Errorf("Sqrt(9) failed. Got %v, expected 3.", v)
}
}
输出如下所示
这里执行时就会报错,如果执行代码发现的❎,一方面可以使用goland自身的debug,此外还可以使用GDB
进行调试,是一个由 GNU 开源组织发布的、Unix/Linux 操作系统下的、基于命令行的、功能强大的程序调试工具,Go 语言编译后的二进制文件支持通过 GDB 进行调试,比如上篇教程通过 go build calc 编译出来的可执行文件 calc,就可以直接用以下命令以调试模式运行
gdb calc
注:Windows 系统不支持该工具,Mac 下可以通过 brew install gdb
命令安装
然后,你就可以通过 GDB 支持的指令以命令行的方式对 Go 代码进行调试了,你可以通过 l 指令查看代码:
GDB
代码调试
要跳到某一行查看通过 l
传入行数即可:
GDB
代码调试
要为某一行设置断点可以通过 b
来实现:
GDB
代码调试
然后通过 run 命令来运行程序,如果是在 Mac 系统上,可能会报下面这个错:
GDB
代码调试
这是因为 Darwin 内核在你没有特殊权限的情况下,不允许调试其它进程。调试某个进程,意味着你对这个进程有完全的控制权限,所以为了防止被恶意利用,它是默认禁止的。允许 gdb 控制其它进程最好的方法就是用系统信任的证书对它进行签名,对应的解决方法参考这里:https://opensource.apple.com/source/lldb/lldb-69/docs/code-signing.txt(中文对应的解决方式)。
进入下一行可以用 n
指令,打印变量可以用 p
指令传入变量名。。。更多指令使用我就不深入展开了,因为对于新手来说,不推荐使用 GDB
进行代码调试,直接使用 GoLand 更友好,不是吗?如果你想探究 GDB
调试的更多用法,请查看对应的官方文档 Debugging Go Code with GDB
跳过测试
既然同pytest有相似之处,go test也支持skip test,当不满足某个条件时跳过该测试case。
func TestSkip(t *testing.T) {
if testing.Short() {
t.Skip("skipping test")
}
time.Sleep(10 * time.Second)
// do something
}
这里引用 GO WEB编程中的一段test,使用-short
标记来避免一个长时间运行的测试用例,类似pytest中的skip.tag标识。
在terminal测试包smiplemath
中执行go test
.输出测试结果,然后接着使用带有tag进行skip
测试
go test -v -cover -short
输出如下:
=== RUN TestADD
--- PASS: TestADD (0.00s)
=== RUN TestSqrt
--- PASS: TestSqrt (0.00s)
=== RUN TestNegativeSqrt
sqrt_test.go:17: Sqrt(9) failed. Got -9223372036854775808, expected 3.
--- FAIL: TestNegativeSqrt (0.00s)
=== RUN TestSkip
sqrt_test.go:23: skipping test
--- SKIP: TestSkip (0.00s)
FAIL
coverage: 100.0% of statements
exit status 1
FAIL calc/simplemath 0.495s
使用-options
参数是,最重要的是查阅help
go help test
// go help testflag
-v
Verbose output: log all tests as they are run. Also print all
text from Log and Logf calls even if the test succeeds.
并行测试
或者说是并发测试我理解也是可以的,尤其是在e2e测试中,由于每个case是独立的(前置条件,步骤和预期结果),如果一个case执行时间为2s,则并行执行可以较能发挥出机器的优势,见如下
package simplemath
import (
"testing"
"time"
)
func TestAdd1(t *testing.T) {
sum := Add(1 ,2)
time.Sleep(1 * time.Second)
if sum != 3 {
t.Errorf("Add(1, 2) failed. Got %v, expected 3.", sum)
}
}
func TestAdd2(t *testing.T) {
sum := Add(2 ,3)
time.Sleep(2 * time.Second)
if sum != 5 {
t.Errorf("Add(2, 3) failed. Got %v, expected 5.", sum)
}
}
func TestAdd3(t *testing.T) {
sum := Add(3 ,4)
time.Sleep(3 * time.Second)
if sum != 7 {
t.Errorf("Add(3, 4) failed. Got %v, expected 7.", sum)
}
}
输出结果如下所示
=== RUN TestADD
--- PASS: TestADD (0.00s)
=== RUN TestAdd1
--- PASS: TestAdd1 (1.00s)
=== RUN TestAdd2
--- PASS: TestAdd2 (2.00s)
=== RUN TestAdd3
--- PASS: TestAdd3 (3.00s)
PASS
ok calc/simplemath 6.144s
这样不算调度时间,整体运行时间 > 6s,先让使用testing.T
的Parallel
函数,执行
go test -v -short -parallel 3
基准测试
Go的 testing
包支持两种类型的测试,一种是用于检验程序功能性的功能测试(functional testing),而另一种则是用于**查明任务单元性能
**的基准测试(benchmarking
)。在上一节学习过如何进行功能测试之后,这一节我们将要学习如何进行基准测试。关于基准测试参考基准测试介绍
跟单元测试一样,基准测试用例也需要放置到以 _test.go
为后缀的文件中,并且每个基准测试函数都需要符合以下格式:
func BenchmarkXxx(*testing.B) { ... }
func BenchmarkExercise(b *testing.B) {
for i := 0; i < b.N; i++ {
fmt.Println("testest")
funcitonModule()
}
}
循环执行解码函数,以便对其进行b.N 次基准测试.在Go语言中进行基准测试是非常直观的:测试程序要做的就是将被测试的代码执行 b.N 次,以便准确地检测出代码的响应时间,其中 b.N 的值将根据被执行的代码而改变。比如,在上面展示的基准测试例子中,测试程序就将 funcitonModule
函数执行了 b.N 次。
为了运行基准测试用例,用户需要在执行 go test 命令时使用基准测试标志 -bench
,并将一个正则表达式用作该标志的参数,从而标识出自己想要运行的基准测试文件。当我们需要运行目录下的所有基准测试文件时,只需要把点( .) 用作 -bench
标志的参数即可:
go test -v -cover -short –bench .