专栏首页Go语言指北Go高阶指南19,测试功能详解-下

Go高阶指南19,测试功能详解-下

书接上文,测试功能详解第二篇,本文主要介绍 Go 的子测试和 main 测试。

子测试

子测试可以使多个测试函数共用部分代码,比如有两个测试函数 A 和 B,有相同的初始化程序,使用子测试函数可以将A、B函数合并到一个函数中,对于它们相同的初始化程序便可以提取出来合并到一起。我们举例说明:

目录结构如下所示:

[ceshi]
  |--[gotest]
          |--subunit.go
  |--[gotest_test]
          |--subunit_test.go

源代码文件 unit.go 代码:

package gotest

func Add(a, b int) int {
 return a + b
}

测试文件 unit_test.go 代码:

package gotest_test

import (
 "testing"
 "ceshi/gotest"
)

// sub1 为子测试,只做加法测试
func sub1(t *testing.T) {
 var a = 1
 var b = 2
 var expected = 3

 actual := gotest.Add(a, b)
 if actual != expected {
  t.Errorf("Add(%d, %d) = %d; expected: %d", a, b, actual, expected)
 }
}

// sub2 为子测试,只做加法测试
func sub2(t *testing.T) {
 var a = 1
 var b = 2
 var expected = 3

 actual := gotest.Add(a, b)
 if actual != expected {
  t.Errorf("Add(%d, %d) = %d; expected: %d", a, b, actual, expected)
 }
}

// TestSub 内部调用sub1、sub2 子测试
func TestSub(t *testing.T) {
 // setup code

 t.Run("A=1", sub1)
 t.Run("A=2", sub2)

 // tear-down code
}

执行测试:

$ go test subunit_test.go  -v
=== RUN   TestSub
=== RUN   TestSub/A=1
=== RUN   TestSub/A=2
--- PASS: TestSub (0.00s)
    --- PASS: TestSub/A=1 (0.00s)
    --- PASS: TestSub/A=2 (0.00s)
PASS
ok      command-line-arguments  1.142s

示例中 TestSub() 通过 t.Run() 依次执行了两个子测试。t.Run() 函数声明如下:

func (t *T) Run(name string, f func(t *T)) bool

  • 参数: name 为子测试的名字, f 为子测试函数;
  • Run() 一直阻塞到 f 执行结束后才返回,返回值为 f 的执行结果。
  • Run() 会启动新的协程来执行 f ,并阻塞等待 f 执行结束才返回,除非 f 中使用了 t.Parallel() 设置子测试为并发。
  • 示例中 TestSub() 把两个子测试合并起来,可以共享 setuptear-down 部分的代码。

子测试的命名

  1. Run() 方法第一个参数为子测试的名字;
  2. 实际上子测试的内部命名规则为:”<父测试名字>/<传递给Run的名字>“。
  3. 例如,传递给 Run() 的名字是“A=1”,那么子测试名字为“TestSub/A=1”。由上面的命令行输出中也可以看出。

过滤筛选

我们可以根据测试的名字来过滤一部分测试,例如使用 -run Sub/A=1 参数达到只执行 “A=1” 的子测试

$ go test subunit_test.go  -v -run Sub/A=1
=== RUN   TestSub
=== RUN   TestSub/A=1
--- PASS: TestSub (0.00s)
    --- PASS: TestSub/A=1 (0.00s)
PASS
ok      command-line-arguments  0.633s

子测试并发

子测试可以使用 t.Parallel() 来指定并发,但是这样就不能共享 setup 和 teardown 部分程序了,因为执行顺序很可能是setup->子测试1->teardown->子测试2。

如果子测试可能并发,则可以把子测试通过 Run() 再嵌套一层, Run() 可以保证其下的所有子测试执行结束后再返回。

package gotest_test

import (
 "testing"
 "time"
)

// 并发 sub1 子测试演示
func parallelSub1(t *testing.T) {
 t.Parallel()
 time.Sleep(2 * time.Second)
 //...
}

// 并发 sub2 子测试演示
func parallelSub2(t *testing.T) {
 t.Parallel()
 time.Sleep(1 * time.Second)
 //...
}

// TestSub 内部调用sub1、sub2子测试
func TestSubParallel(t *testing.T) {
 // setup code

 t.Run("group", func(t *testing.T){
  t.Run("Test1", parallelSub1)
  t.Run("Test2", parallelSub2)
 })

 // tear-down code
}
  • 子测试是并发执行的(Test1最先被执行却最后结束)
  • tear-down在所有子测试结束后才执行
$ go test subparallel_test.go -v run SubParallel
=== RUN   TestSubParallel
    subparallel_test.go:25: setup code
=== RUN   TestSubParallel/group
=== RUN   TestSubParallel/group/Test1
=== PAUSE TestSubParallel/group/Test1
=== RUN   TestSubParallel/group/Test2
=== PAUSE TestSubParallel/group/Test2
=== CONT  TestSubParallel/group/Test1
=== CONT  TestSubParallel/group/Test2
=== CONT  TestSubParallel
    subparallel_test.go:33: tear-down code
--- PASS: TestSubParallel (2.00s)
    --- PASS: TestSubParallel/group (0.00s)
        --- PASS: TestSubParallel/group/Test2 (1.01s)
        --- PASS: TestSubParallel/group/Test1 (2.00s)
PASS
ok      command-line-arguments  2.584s

总结

  • 子测试适用于单元测试和性能测试;
  • 子测试可以控制并发;
  • 子测试可以共享setup和tear-down;

Main测试

Main测试,即声明一个 :func TestMain(m *testing.M) ,参数类型为 testing.M 指针,当前测试程序将不是直接执行各项测试,而是将测试交给 TestMain 调度。

源代码文件 unit.go 代码:

package gotest

func Add(a, b int) int {
 return a + b
}

测试文件 unit_test.go 代码:

package gotest_test

import (
 "ceshi/gotest"
 "fmt"
 "os"
 "testing"
)

func TestAdd(t *testing.T){
 var a = 1
 var b = 2
 var expected = 3

 actual := gotest.Add(a, b)
 if actual != expected {
  t.Errorf("Add(%d, %d) = %d; expected: %d", a, b, actual, expected)
 }
}

// TestMain 用于主动执行各种测试,可以测试前后做setup和tear-down操作
func TestMain(m *testing.M) {
 fmt.Println("TestMain setup.")
 retCode := m.Run() // 执行测试,包括单元测试、性能测试和示例测试
 fmt.Println("TestMain tear-down.")
 os.Exit(retCode)
}

执行测试

$ go test unit_test.go -v
TestMain setup.
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
TestMain tear-down.
ok      command-line-arguments  0.593s
  • Main 测试课用于在整个测试程序做一些全局的setup和Tear-down
  • 日志打印的两行分别对应 Setup 和 Tear-down 代码;
  • m.Run()即为执行所有的测试,m.Run() 的返回结果通过 os.Exit() 返回。
  • 如果所有测试均通过测试,m.Run() 返回0,否同 m.Run() 返回1,代表测试失败。
  • TestMain执行时,命令行参数还未解析,如果测试程序需要依赖参数,可以使用 flag.Parse() 解析参数,m.Run()方法内部还会再次解析参数,此处解析不会影响原测试过程。

图片及部分相关技术知识点来源于网络搜索,侵权删!

参考资料:

https://my.oschina.net/renhc/blog/3009124

https://blog.csdn.net/weixin_33827965/article/details/91699652

https://blog.csdn.net/ma2595162349/article/details/112437975

本文分享自微信公众号 - 微客鸟窝(gophpython),作者:有码无尘

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-09-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Go高阶18,go语言测试功能详解-上

    您诸位好啊,我是“菜鸟哥”--无尘,最早能看到这篇文章的小伙伴,也应该是中秋假期第一天了,首先祝大家中秋愉快,也感谢大家这么久以来的关注和支持。

    微客鸟窝
  • Go开发者路线图2019,请收下这份指南

    Go是Google开发的一种静态、强类型、编译型、并发型,并具有垃圾回收功能的类C编程语言。2009以开源项目的形式发布,2012年发布1.0稳定版本,距今已经...

    AI科技大本营
  • Go 语言学习路线来啦

    时不时的有人问我一些关于 Go 语言学习路线、学习资源方面的问题,这篇文章就来详细说一说。借此希望给那些正在学习,或是想学习 Go 语言的朋友一些帮助。

    roseduan
  • 聊聊阿秀过去三年间做的最正确的一件事 | 快来薅羊毛

    我买的大部分是技术书,也有一些非技术书,比如《明朝那些事儿》、《平凡的世界》之类的。

    拓跋阿秀
  • 书单丨4本书教你如何优雅地迁移上云

    《云原生Java:Spring Boot、Spring Cloud与Cloud Foundry弹性系统设计》

    博文视点Broadview
  • 【阅读清单】有奖内测体验活动

    完成了内测体验的小伙伴,可以填写问卷,活动结束后我们将统一进行审核,审核结束就进行礼品发放。问卷链接:https://wj.qq.com/s2/7433898/...

    云加社区
  • Golang学习资料大全

    咻咻ing
  • 如何在Ubuntu上使用Jenkins自动构建

    Jenkins是一个开源自动化服务器,允许您构建管道以自动化构建,测试和部署应用程序的过程。在本指南中,您将实施基本工作流程,以加快持续集成和持续交付(CI /...

    Techeek
  • 适合 Go 新手学习的开源项目——在 GitHub 学编程

    故事要从 2007 年说起。因为受够了 C++ 煎熬的 Google 首席软件工程师 Rob Pike 召集 Robert Griesemer 和 Ken Th...

    HelloGitHub
  • Web前端开发推荐阅读书籍、学习课程下载

    学校里没有前端的课程,那如何学习JavaScript,又如何使自己成为一个合格的前端工程师呢?

    慕白
  • 企业FRP安全实践

    看到的第一反应是无论此事真假,但如果发生在我司,安全部有没有能力去发现?于是,本着守望互助和发散思考的原则研究了一波内网端口映射到公网软件,恰好看到朋友圈已经有...

    FB客服
  • Go 语言基础入门教程 —— 单元测试、问题定位与代码调试

    在 Go 语言中,支持为功能模块编写单元测试代码,继续以上篇教程构建的计算器项目为例,在 simplemath 包中,我们可以为每一个运算模块编写了对应的单元测...

    学院君
  • 开源 LoRaWAN Network Server 项目 | ChirpStack 学习笔记

    最近在研究一些 LoRaWAN 新功能时又翻到了这个项目,发现一些新特性在这个项目中已经实现了,惊讶作者 brocaar 的效率之高。这一两年也陆续收到不少朋友...

    twowinter
  • 如何运用Wercker开发与部署应用程序

    Wercker 是一款软件自动化工具,旨在为开发者和企业改善持续集成(CI, Continuous-Integration)和持续交付(CD, Continuo...

    StoneDemo
  • NLP简报(Issue#9)

    RONEC[1]是罗马尼亚语的命名实体语料库,在约5000个带注释的句子中包含超过26000个实体,属于16个不同的类。这些句子摘自无版权的报纸,内容涉及多种样...

    NewBeeNLP
  • Github开源免费编程书籍

    时见疏星
  • 安全左移理念,鹅厂 DevSecOps 如何实践?

    ? 作者:yuyangzhou、dexyfruan,腾讯 TEG 应用运维安全工程师 引子 随着 DevOps 模式的落地,快字当头。研效提速也意味着出现安...

    腾讯技术工程官方号
  • 自动的内存管理系统实操手册——Golang垃圾回收篇

    ? 导语 | 现代高级编程语言管理内存的方式分自动和手动两种。手动管理内存的典型代表是C和C++,编写代码过程中需要主动申请或者释放内存;而PHP、Java ...

    腾小云
  • 我的书单

    songleo

扫码关注云+社区

领取腾讯云代金券