• 如何用好Go的测试黑科技


    测试是每一个开发人员都需要掌握的技能,尽管你不需要像测试人员那么专业,但你也应该尽可能的做到那么专业,据我了解到我身边的一些Go开发人员,他们对Go的测试仅仅局限于写一个_test.go 测试文件,对执行方法进行测试,然后在goland的Ide中右键run方法运行,观测结果是否为绿色,仅此而已,我想说的是这只是一些皮毛,所以今天分享一些Go的测试技能,希望大家有收获。

    Go测试用例

    Go的测试文件命名规则为xxx_test.go,其中xxx是需要测试的源代码文件的名称。在test文件中,可以编写测试函数,Go的测试函数整理分为4种,如下,其中XXX是需要测试的方法名称

    1. 单元测试:TestXXX(t *testing.T)
    2. 基准测试:BenchmarkXXX(b *testing.B)
    3. Main函数测试:TestMain(m *testing.M)
    4. 控制台测试 ExampleXXX()

    假设我们有一个array_utils.go的源代码文件,包名为array_utils,我们在该包创建一个测试文件,名称为:array_utils_test.go文件,源代码文件中有一个求最大子序列和的方法,我们针对该方法测试,如下代码,_test.go文件中可以有任意多个测试方法,这些测试方法的合集被称作测试套件。

    我们的源码文件如下:
     package array_utils
     //求最大子序列和 (就是说子序列加起来和最大)
     func FindMaxSeqSum(array []int) int {
       SeqSum := make([]int, 0) // 存储子序列和
       // 初始子序列和为 数组下标为0的值
       SeqSum = append(SeqSum, array[0])
       for i := 1; i < len(array); i++ {
          if array[i] > SeqSum[i-1]+array[i] {
             SeqSum = append(SeqSum, array[i])
          } else {
             SeqSum = append(SeqSum, SeqSum[i-1]+array[i])
          }
       }
       max := SeqSum[0]
       for j := 1; j < len(SeqSum); j++ {
          if SeqSum[j] > SeqSum[j-1] {
             max = SeqSum[j]
          }
       }
       fmt.Println(max) //打印结果
       return max
    }
    
    我们的测试文件如下
    package array_utils
    
    import (
       "fmt"
       "os"
       "testing"
    )
    //TestMain会在下面所有测试方法执行开始前先执行,一般用于初始化资源和执行完后释放资源
    func TestMain(m *testing.M) {
       fmt.Println("初始化资源")
       result := m.Run() //运行go的测试,相当于调用main方法
       fmt.Println("释放资源")
       os.Exit(result) //退出程序
    }
    //单元测试
    func TestFindMaxSeqSum(t *testing.T) {
       sum := FindMaxSeqSum([]int{1, 3, -9, 6, 8, -19})
       if sum == 14 {
          t.Log("successful")
       } else {
          t.Error("failed")
       }
    }
    //基准测试
    func BenchmarkFindMaxSeqSum(b *testing.B) {
       for i := 0; i < b.N; i++ {
          FindMaxSeqSum([]int{1, 3, -9, 6, 8, -19})
       }
    }
    //这个验证的是FindMaxSeqSum方法控制台输出的max和OutPut后面的14是否一致,如果相同,则表示验证通过,否则测试用例失败
    func ExampleFindMaxSeqSum() {
       FindMaxSeqSum([]int{1, 3, -9, 6, 8, -19})
       // OutPut: 14
    }
    

    我们通过命令go test来运行这段测试代码,进入到array_utils包下面,go test会遍历当前包下所有的xxx_test.go中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件,结果如下:

    初始化资源
    === RUN   TestFindMaxSeqSum
    14
    --- PASS: TestFindMaxSeqSum (0.00s)
        array_utils_test.go:16: successful
    PASS
    释放资源
    

    Go test 有两种运行模式

    本地目录模式,在没有包参数(例如 go test 或 go test -v )调用时发生。在此模式下, go test 编译当前目录中找到的包和测试,然后运行测试二进制文件。在这种模式下,caching 是禁用的。在包测试完成后,go test 打印一个概要行,显示测试状态、包名和运行时间

    1. go test 当软件包属于$GOPATH时,不需要从 Go Module 内部执行此命令,就进行测试

    包列表模式,在使用显示包参数调用 go test 时发生(例如 go test math , go test ./... 甚至是 go test .)。在此模式下,Go测试编译并测试在命令上列出的每个包。如果一个包测试通过, go test 只打印最终的 ok 总结行。如果一个包测试失败, go test 将输出完整的测试输出。如果使用 -bench 或 -v 标志,则 go test 会输出完整的输出,甚至是通过包测试,以显示所请求的基准测试结果或详细日志记录

    1. go test math ,指的是测试Go的SDK的math包中的test文件
    2. go test ./... , 指的是测试递归测试当前目录下的所有test文件,因为当前目录下还有还有子文件夹,子文件夹下面还有子文件夹。
    3. go test . 测试当前目录中的软件包
    4. go test ./tranform 来测试 ./tranform 目录中的包

    在包列表模式下,Go缓存成功的测试结果,以避免重复运行相同的测试。每当 GO 在包上运行测试时,Go都会创建一个测试二进制文件并运行它,如果要全局禁用缓存,可以将GOCACHE环境变量设置为off,

    set GOCACHE=off  //关闭,go1.12版本后必须打开,否则编译器报错
    set GOCACHE=on  //开启
    go clean -testcache  //手动清除缓存
    

    Go的test常用参数实践

    上面我们只是执行了go test的命令,关于go test可能的flag还有很多,不同的flag其对应的功能不同,接下来我们来实践一下。

    基础功能参数

    go test -c 生成用于运行测试的可执行文件,但不执行,在window平台下生成的是.exe文件,截图如下

    go test -i 安装/重新安装运行测试所需的依赖包,但不编译和运行测试代码。

    go test -o array_utils.test.exe 运行指定的的可执行的测试文件

    go test -v 输出打印有关测试函数的其它信息

    go test -v array_utils_test.go array_utils.go 测试指定的的文件

    go test -v array_utils_test.go array_utils.go -test.run TestFindMaxSeqSum 测试指定文件的指定方法

    go test -v -run=TestFindMaxSeqSum 测试指定文件的指定方法,-run后面可以匹配正则表达式,这个指的是测试名字等于TestFindMaxSeqSum的方法,如果是多个方法的话,可以使用|来隔开方法名。

    代码覆盖率

    由单元测试的代码,触发运行到的被测试代码的代码行数占所有代码行数的比例,被称为测试覆盖率,代码覆盖率不一定完全精准,但是可以作为参考,可以帮我们测量和我们预计的覆盖率之间的差距

    go test -cover 生成代码测试覆盖率 ,coverage: 8.1% of statements

    go test -v -coverprofile=c.out 将生成的代码测试覆盖率放入一个文件,然后运行下面的命令可以将c.out文件转换成一个html文件用浏览器阅读,截图如下,no coverage 代表没有覆盖的代码,high coverage代表高覆盖率的代表,一个红色,一个绿色,这里红色的截图上没体现出来,大家可本地试验一下。

    go tool cover -html=c.out -o=tag.html
    

    go test -covermode=set 覆盖测试模式,有三种值set,count,atomic,其中set代表的是这个语句运行吗?count代表的是这个语句执行多少次,atomic代表的是多线程正确使用的,耗资源的。

    基准测试

    go test默认情况下只会运行单元测试,那么基准测试如何执行呢?接下来一起看看。

    go test -bench=. 执行当前测试包下的基准测试方法,在执行过程中会根据实际case的执行时间是否稳定会增加b.N的次数,要注意如果是要测试一个非稳态的函数,那么它可能永远也执行不完,记住-bench后面跟的是正则表达式

    这是执行结果,-8指的是运行时对应的 GOMAXPROCS 的值,5000000指的是for循环的次数,249 ns/op 指的是每一次循环耗时239纳秒
    BenchmarkFindMaxSeqSum-8         5000000               249 ns/op
    

    go test -run=none -bench=. 通过指定方法名称为none来过滤掉单元测试,只执行基准测试的方法,当然也可以根据-bench后面的正则表达式来匹配。

    go test -benchtime=3s -bench=. 在持续时间3s内运行每个基准测试

    go test -benchmem -bench=. 打印基准测试时的内存分配

    120 B/op代表每次操作消耗120B内存(1kb=1024b),  4 allocs/op 代表每次操作分配内存的次数
    BenchmarkFindMaxSeqSum-8   5000000   300 ns/op    120 B/op    4 allocs/op
    

    go test -count=2 -bench=. 执行指定次数的基准测试,在-count=1时相当于禁用缓存

    go test -cpu=1 -bench=. 设置指定的cpu数量来进行基准测试,可以指定多个不同的cpu个数列别,比如:-cpu=1,2,4

    其它一些参数控制

    go test -timeout=3s默认情况下,测试执行超过10分钟就会超时而退出,我们可以通过这个时间指定超时时间

    go test -parallel=2 当测试使用t.Parallel()方法将测试转为并发时,将受到最大并发数的限制,默认情况下最多有GOMAXPROCS个测试并发,其他的测试只能阻塞等待,这个可以用来并发安全的测试。

    go test -short 缩短长时间运行的测试的测试时间。默认关闭

    go test -v -cpuprofile=cpuprof.out 生成cpuprof的文件,通过运行下面的命令可以查看cpuprof的文件,默认是在控制台查看,当然也可以web界面查看,这不是本篇文章的重点,后面会单说。

    go tool pprof prof.out
    

    go test -trace trace.out 在退出之前,将执行跟踪写入指定文件。

    go test -race 检测并发情况下数据竞争的问题,这个的使用比较复杂,后面也会单写文章来介绍。

    testing 的变量

    test.short : 一个快速测试的标记,在测试用例中可以使用 testing.Short() 来绕开一些测试
    test.outputdir : 输出目录
    test.coverprofile : 测试覆盖率参数,指定输出文件
    test.run : 指定正则来运行某个 / 某些测试用例
    test.memprofile : 内存分析参数,指定输出文件
    test.memprofilerate : 内存分析参数,内存分析的抽样率
    test.cpuprofile : cpu 分析输出参数,为空则不做 cpu 分析
    test.blockprofile : 阻塞事件的分析参数,指定输出文件
    test.blockprofilerate : 阻塞事件的分析参数,指定抽样频率
    test.timeout : 超时时间
    test.cpu : 指定 cpu 数量
    test.parallel : 指定运行测试用例的并行数

    还有很多,需要读者们自行研究,总结,分享,可以留言区讨论

    testing.T 和 testing.B

    testing 包内的结构

    B : 压力测试
    BenchmarkResult : 压力测试结果
    Cover : 代码覆盖率相关结构体
    CoverBlock : 代码覆盖率相关结构体
    InternalBenchmark : 内部使用的结构
    InternalExample : 内部使用的结构
    InternalTest : 内部使用的结构
    M : main 测试使用的结构
    PB : Parallel benchmarks 并行测试使用结果
    T : 普通测试用例
    TB : 测试用例的接口

    testing包的方法

    如果有帮助,关注下公众号,阅读更多精彩文章

    ![](https://img2018.cnblogs.com/blog/706455/201909/706455-20190911210708072-261554801.jpg)

  • 相关阅读:
    [Err] 1064
    maven项目警告: Using platform encoding (UTF-8 actually) to copy filtered resources
    maven中引入oracle驱动报错Missing artifact com.oracle:ojdbc14:jar:10.2.0.4.0
    springMVC 中url后缀使用html,不能返回json数据,否则会报406错误
    网站并发量的计算方法
    win7旗舰版显示不了文件扩展名
    Java获取mysql数据库元数据
    spring中 context:property-placeholder 导入多个独立的 .properties配置文件
    Could not autowire field: private java.lang.Integer com.taotao.sso.service.impl.UserServiceImpl.SSO_
    linux Nginx服务开机自启
  • 原文地址:https://www.cnblogs.com/sy270321/p/12215375.html
Copyright © 2020-2023  润新知