golang⽰例测试example_Go36-23,24,25-单元测试
单元测试
对于程序或软件的测试分很多种,⽐如:单元测试、API测试、集成测试、灰度测试等。这⾥主要针对单元测试进⾏讲解。
Go 语⾔是⼀门很重视程序测试的语⾔,它不但⾃带了testing包,还有专门⽤于程序测试的命令go test。要想真正⽤好⼀个⼯具,就需要了解它的核⼼逻辑。
测试源码⽂件
单元测试,⼜称程序员测试。就是程序员程序员们本该做的⾃我检查的⼯作之⼀。
我们可以为Go程序编写3类测试:
功能测试(test)
基准测试(benchmark),也称性能测试
⽰例测试(example)
前两类测试,从名称上就应该可以猜到它们的⽤途。⽰例测试,严格来讲也是⼀种功能测试,只不过它更关注程序打印出来的内容。
⼀般情况下,⼀个测试源码⽂件只会针对于某个命令源码⽂件,或库源码⽂件做测试,所以⼀般并且也应该把他们放在同⼀个代码包内。
测试源码⽂件的主名称应该以被测试源码⽂件的主名称为签到,并且必须以“_test”为后缀。
每个测试源码⽂件都必须⾄少包含⼀个测试函数。并且,从语法上讲,每个测试源码⽂件中,都可以包含⽤来做任何⼀类测试的测试函数,就是说把上⾯说的3类测试函数都塞进去也没问题,只要把控好测试函数的分组和数量就可以了。
我们可以依据这些测试函数针对的不同程序实体,把他们分成不同额逻辑组。并且利⽤注释以及帮助类的变量或函数来做分割。同时,还可以依据被被测试的⽂件中程序实体的先后顺序来安排测试源码⽂件中测试函数的顺序。
测试函数的名称和签名
对于测试函数的名称和签名,Go语⾔也有明⽂的规定:
功能测试函数,名称必须以Test为前缀,并且只有⼀个*testing.T类型的参数。
性能测试函数,名称必须以Benchmark为前缀,并且只有⼀个*testing.B类型的参数。
⽰例测试函数,名称必须以Example为前缀,对参数没有强制规定。
go test的主要流程
前⾯两⼩节的内容,其实是go test命令的基本规则。只有测试源码⽂件的名称对了,测试函数的名称和签名也对了,在运⾏go test命令的时候,其中的测试代码才会被运⾏。接着讲主要流程。
当go test命令在开始运⾏时,会先做⼀下准备⼯作,⽐如:确定内部需要⽤到的命令,检查指定的代码包或源码⽂件的有效性,以及判断我们给予的标记是否合法,等等。
在准备⼯作完成后,go test命令就会针对每个被测试代码包,依次的进⾏构建、执⾏包中符合要求的测试函数,清理临时⽂件,打印测试结果。这就是通常情况下的主要测试流程。
这⾥的“依次”⼆字,对于每个被测试代码包,go test命令会串⾏地执⾏测试流程中的每个步骤。但是为了加快测试速度,通常会并发的对多个被测试代码包进⾏功能测试。但是在最后打印测试结果的时候,会依照给定的顺序逐个打印。
另⼀个⽅⾯,由于并发的测试会让性能测试的结果存在偏差,所以性能测试⼀般都是串⾏进⾏的。具
体说就是在所有构建步骤都做完之后,go test命令才会真正地开始进⾏性能测试。并且下个⼀个代码包的性能测试的进⾏,会等到上⼀个代码包的性能测试的结果打印完成才会开始,并且性能测试函数的执⾏也都会是串⾏的。
功能测试
先给出⽰例,然后主要讲测试规则和流程
功能测试⽰例
这⾥把⽰例单独给出,⾸先是命令源码⽂件:
//
package main
import (
"errors"
"flag"
"fmt"
)
var name string
func init() {
flag.StringVar(&name, "name", "bobody", "Your Name:")
}
func main() {
flag.Parse()
greeting, err := hello(name)
if err != nil {
fmt.Printf("ERROR: %s\n", err)
return
}
fmt.Println(greeting, introduce())
}
func hello(name string) (string, error) {
if name == "" {
return "", errors.New("empyt name")
}
return fmt.Sprintf("Hello %s.", name), nil
}
func introduce() string {
return "Welcome"
}
然后是测试源码⽂件,这⾥对⽂件名和函数名称都是有要求的。看函数名称这⾥都是功能测试函数://
package main
import (
"fmt"
"testing"
)
func TestHello(t *testing.T) {
var name string
greeting, err := hello(name)
if err == nil {
t.Errorf("The error is nil, but it should not be. (name=%q)", name)
}
if greeting != "" {
t.Errorf("Nonempty greeting, but it should not be. (name=%q)", name) }
name = "Clark"
greeting, err = hello(name)
if err != nil {
t.Errorf("The error is not nil, but it should be. (name=%q)", name)
}
if greeting == "" {
t.Errorf("Empty greeting, but it should not be. (name=%q)", name)
}
expected := fmt.Sprintf("Hello %s.", name)
if greeting != expected {
t.Errorf("greeting: %q, expected: %q, not same.", greeting, expected) }
t.Logf("The expected greeting is %q.\n", expected)
}
func TestIntroduce(t *testing.T) {
intro := introduce()
expected := "Welcome"
if intro != expected {
t.Errorf("intro: %q, expected: %q, not same", intro, expected)
}
t.Logf("The expected introduce is %q.\n", expected)
}
功能测试的结果
先来看下⾯的测试命令和结果:
PS H:\Go\src> go test Go36\article23\example01
ok Go36/article23/example01 0.051s
PS H:\Go\src>
在这⾥,输⼊了命令go test Go36\article23\example01,表⽰想对这个导⼊路径的代码包进⾏测试。另外go test命令,默认执⾏当前⽬录下以_结尾的测试⽂件,所以可以不带后⾯的参数。
命令的下⾯⼀⾏,就是此次测试的简要结果。简要结果有三块内容:
最左边的ok表⽰此次测试成功
中间的内容,显⽰了被测代码包的导⼊路径
最右边,展现了此次对该代码包测试所耗的时间
缓存
对于最右边的时间,有时候可能是这样的:
ok Go36/article23/example01 (cached)
这表明,由于测试代码与被测试代码都没有任何变动,所以go test命令直接把之前缓存测试成功的结果打印出来了。关于这个缓存,可以运⾏下⾯的命令来查看缓存⽬录的路径:
go env GOCACHE
可能windows系统上没有缓存?我在win10系统上貌似是没有缓存。go env⾥也没有GOCACHE这个环境变量。尝试⼿动去系统⾥加上这个变量,再⽤go env也是显⽰不出来,系统重启也试过了
缓存的数据可以正确的反应出测试时的各种源码⽂件、构建环境、编译器选项等情况。⼀旦有任何变
动,缓存的数据就会失效,go test命令就会再次真正地执⾏操作。go命令会定期地删除最近未使⽤的缓存数据,不过也是可以⼿动删除所有缓存数据的,命令如下:
go clean -cache
上⾯的命令清除的是go命令所有的缓存,对于测试成功的结果,go命令也会缓存。可以只删除所有测试结果的缓存,⽽不删除任何构建结果的缓存。命令如下:
go clean -testcache
这⾥的命令也有问题,打印go clen的帮助如下:
PS H:\Go\src\Go36> go clean --help
usage: clean [-i] [-r] [-n] [-x] [build flags] [packages]
就上⾯这些参数,这⾥的命令参数貌似都是没有的,不清楚是版本问题,还是系统问题。
这⾥是⽹上查到的⽅法:显⽰禁⽤缓存的惯⽤⽅法是使⽤-count=1标志。就是作为参数加到go test命令后⾯。
总之,不⽤太在意缓存数据的存在,因为这并不会妨碍go test命令打印正确的测试结果。
测试失败
如果功能测试的函数的参数名称为t,那么调⽤t.Fail⽅法,可以使测试失败。在之前的测试代码的最后,再新增⼀个功能测试函数TestFail,具体如下:
func TestFail(t *testing.T) {
t.Fail()
t.Log("测试失败...")
}
现在再运⾏测试,就会显⽰测试失败,具体如下:
PS G:\Steed\Documents\Go\src\Go36\article24\example01> go test
--- FAIL: TestFail (0.00s)
:43: 测试失败...
FAIL
exit status 1
FAIL Go36/article24/example01 0.115s
PS G:\Steed\Documents\Go\src\Go36\article24\example01>
上⾯的内容显⽰,TestFail函数的测试失败。
对于失败测试的结果,是不会进⾏缓存的,所以每次测试都是全新的结果。
t.FailNow()
这⾥还有⼀个⽅法:t.FailNow()。
与t.Fail()不同,在t.FailNow()执⾏后,当前函数会⽴即终⽌执⾏。t.FailNow⽅法只是终⽌它所在的函数,不会影响到其他函数的处理。但是当前函数执⾏到t.FailNow()之后,后⾯的代码就没有执⾏的的机会了。如果把上⾯例⼦中的t.Fail()替换成t.FailNow(),则不会打印测试⽇志,因为t.Log没有机会执⾏。go语言能做什么
测试⽇志
如果测试失败了,那么go test会将导致失败的测试函数中的常规测试⽇志⼀并打印出来,就是t.Log⽅法调⽤的内容。
t.Log⽅法以及t.Logf⽅法,就是打印常规的测试⽇志。只有在测试失败的时候才当测试成功的时候,不打印这类⽇志。默认在测试成功的时候不打印这类⽇志,不过可以加上-v参数在成功时也打印测试⽇志。
t.Error 和 t.Errorf
如果想在测试失败的通常打印失败测试⽇志,那么可以直接调⽤t.Error或t.Errorf⽅法。相当于t.Log或t.Logf和t.Fail⽅法的连续调⽤。
t.Fatal 和 t.Fatalf
这两个⽅法的作⽤是在打印失败错误⽇志后⽴即终⽌当前测试函数的执⾏并宣告失败。就是相当于调⽤了t.FailNow⽅法。
性能测试
性能测试与功能测试的结果有很多相似的地⽅,先把⽰例代码准备好,然后关注性能测试结果⾥的特殊之处。
测试⽰例
先准备⽤来做性能测试的包,这⾥⽤不同的⽅法来实现斐波那契数列。⼀个⽤递归⽅法,效率⽐较差,还有⼀个是for循环实现的,逻辑稍微复杂⼀点但是效率更⾼:
// Go36/article24/example02/
package fibonacci
func FibRescuvie (x uint) uint64 {
if x > 93 { return 0 } // uint64最多可以存第93个数,再⾼就溢出了
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论