Go testing.common公共类源码剖析
为什么80%的码农都做不了架构师?>>>
简介
我们知道单元测试函数需要传递一个testing.T
类型的参数,而性能测试函数需要传递一个testing.B
类型的参数,该参数可用于控制测试的流程,比如标记测试失败等。
testing.T
和testing.B
属于testing
包中的两个数据类型,该类型提供一系列的方法用于控制函数执行流程,考虑到二者有一定的相似性,所以Go实现时抽象出一个testing.common
作为一个基础类型,而testing.T
和testing.B
则属于testing.common
的扩展。
本节,我们重点看testing.common
,通过其成员及方法,来了解其实现原理。
数据结构
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {mu sync.RWMutex // guards this group of fieldsoutput []byte // Output generated by test or benchmark.w io.Writer // For flushToParent.ran bool // Test or benchmark (or one of its subtests) was executed.failed bool // Test or benchmark has failed.skipped bool // Test of benchmark has been skipped.done bool // Test is finished and all subtests have completed.helpers map[string]struct{} // functions to be skipped when writing file/line infochatty bool // A copy of the chatty flag.finished bool // Test function has completed.hasSub int32 // written atomicallyraceErrors int // number of races detected during testrunner string // function name of tRunner running the testparent *commonlevel int // Nesting depth of test or benchmark.creator []uintptr // If level > 0, the stack trace at the point where the parent called t.Run.name string // Name of test or benchmark.start time.Time // Time test or benchmark startedduration time.Durationbarrier chan bool // To signal parallel subtests they may start.signal chan bool // To signal a test is done.sub []*T // Queue of subtests to be run in parallel.
}
common.mu
读写锁,仅用于控制本数据内的成员访问。
common.output
存储当前测试产生的日志,每产生一条日志则追加到该切片中,待测试结束后再一并输出。
common.w
子测试执行结束需要把产生的日志输送到父测试中的output切片中,传递时需要考虑缩进等格式调整,通过w把日志传递到父测试。
common.ran
仅表示是否已执行过。比如,跟据某个规范筛选测试,如果没有测试被匹配到的话,则common.ran为false,表示没有测试运行过。
common.failed
如果当前测试执行失败,则置为true。
common.skipped
标记当前测试是否已跳过。
common.done
表示当前测试及其子测试已结束,此状态下再执行Fail()之类的方法标记测试状态会产生panic。
common.helpers
标记当前为函数为help函数,其中打印的日志,在记录日志时不会显示其文件名及行号。
common.chatty
对应命令行中的-v参数,默认为false,true则打印更多详细日志。
common.finished
如果当前测试结束,则置为true。
common.hasSub
标记当前测试是否包含子测试,当测试使用t.Run()方法启动子测试时,t.hasSub则置为1。
common.raceErrors
竞态检测错误数。
common.runner
执行当前测试的函数名。
common.parent
如果当前测试为子测试,则置为父测试的指针。
common.level
测试嵌套层数,比如创建子测试时,子测试嵌套层数就会加1。
common.creator
测试函数调用栈。
common.name
记录每个测试函数名,比如测试函数TestAdd(t *testing.T)
, 其中t.name即“TestAdd”。 测试结束,打印测试结果会用到该成员。
common.start
记录测试开始的时间。
common.duration
记录测试所花费的时间。
common.barrier
用于控制父测试和子测试执行的channel,如果测试为Parallel,则会阻塞等待父测试结束后再继续。
common.signal
通知当前测试结束。
common.sub
子测试列表。
成员方法
common.Name()
// Name returns the name of the running test or benchmark.
func (c *common) Name() string {return c.name
}
该方法直接返回common结构体中存储的名称。
common.Fail()
// Fail marks the function as having failed but continues execution.
func (c *common) Fail() {if c.parent != nil {c.parent.Fail()}c.mu.Lock()defer c.mu.Unlock()// c.done needs to be locked to synchronize checks to c.done in parent tests.if c.done {panic("Fail in goroutine after " + c.name + " has completed")}c.failed = true
}
Fail()方法会标记当前测试为失败,然后继续运行,并不会立即退出当前测试。如果是子测试,则除了标记当前测试结果外还通过c.parent.Fail()
来标记父测试失败。
common.FailNow()
func (c *common) FailNow() {c.Fail()c.finished = trueruntime.Goexit()
}
FailNow()内部会调用Fail()标记测试失败,还会标记测试结束并退出当前测试协程。 可以简单的把一个测试理解为一个协程,FailNow()只会退出当前协程,并不会影响其他测试协程,但要保证在当前测试协程中调用FailNow()才有效,不可以在当前测试创建的协程中调用该方法。
common.log()
func (c *common) log(s string) {c.mu.Lock()defer c.mu.Unlock()c.output = append(c.output, c.decorate(s)...)
}
common.log()为内部记录日志入口,日志会统一记录到common.output切片中,测试结束时再统一打印出来。 日志记录时会调用common.decorate()进行装饰,即加上文件名和行号,还会做一些其他格式化处理。 调用common.log()的方法,有Log()、Logf()、Error()、Errorf()、Fatal()、Fatalf()、Skip()、Skipf()等。
注意:单元测试中记录的日志只有在执行失败或指定了-v
参数才会打印,否则不会打印。而在性能测试中则总是被打印出来,因为是否打印日志有可能影响性能测试结果。
common.Log(args ...interface{})
func (c *common) Log(args ...interface{}) {c.log(fmt.Sprintln(args...))
}
common.Log()方法用于记录简单日志,通过fmt.Sprintln()方法生成日志字符串后记录。
common.Logf(format string, args ...interface{})
func (c *common) Logf(format string, args ...interface{}) {c.log(fmt.Sprintf(format, args...))
}
common.Logf()方法用于格式化记录日志,通过fmt.Sprintf()生成字符串后记录。
common.Error(args ...interface{})
// Error is equivalent to Log followed by Fail.
func (c *common) Error(args ...interface{}) {c.log(fmt.Sprintln(args...))c.Fail()
}
common.Error()方法等同于common.Log()+common.Fail(),即记录日志并标记失败,但测试继续进行。
common.Errorf(format string, args ...interface{})
// Errorf is equivalent to Logf followed by Fail.
func (c *common) Errorf(format string, args ...interface{}) {c.log(fmt.Sprintf(format, args...))c.Fail()
}
common.Errorf()方法等同于common.Logf()+common.Fail(),即记录日志并标记失败,但测试继续进行。
common.Fatal(args ...interface{})
// Fatal is equivalent to Log followed by FailNow.
func (c *common) Fatal(args ...interface{}) {c.log(fmt.Sprintln(args...))c.FailNow()
}
common.Fatal()方法等同于common.Log()+common.FailNow(),即记录日志、标记失败并退出当前测试。
common.Fatalf(format string, args ...interface{})
// Fatalf is equivalent to Logf followed by FailNow.
func (c *common) Fatalf(format string, args ...interface{}) {c.log(fmt.Sprintf(format, args...))c.FailNow()
}
common.Fatalf()方法等同于common.Logf()+common.FailNow(),即记录日志、标记失败并退出当前测试。
common.skip()
func (c *common) skip() {c.mu.Lock()defer c.mu.Unlock()c.skipped = true
}
common.skip()方法标记当前测试为已跳过状态,比如测试中检测到某种条件,不再继续测试。该函数仅标记测试跳过,与测试结果无关。测试结果仍然取决于common.failed。
common.SkipNow()
func (c *common) SkipNow() {c.skip()c.finished = trueruntime.Goexit()
}
common.SkipNow()方法标记测试跳过,并标记测试结束,最后退出当前测试。
common.Skip(args ...interface{})
// Skip is equivalent to Log followed by SkipNow.
func (c *common) Skip(args ...interface{}) {c.log(fmt.Sprintln(args...))c.SkipNow()
}
common.Skip()方法等同于common.Log()+common.SkipNow()。
common.Skipf(format string, args ...interface{})
// Skipf is equivalent to Logf followed by SkipNow.
func (c *common) Skipf(format string, args ...interface{}) {c.log(fmt.Sprintf(format, args...))c.SkipNow()
}
common.Skipf()方法等同于common.Logf() + common.SkipNow()。
common.Helper()
// Helper marks the calling function as a test helper function.
// When printing file and line information, that function will be skipped.
// Helper may be called simultaneously from multiple goroutines.
func (c *common) Helper() {c.mu.Lock()defer c.mu.Unlock()if c.helpers == nil {c.helpers = make(map[string]struct{})}c.helpers[callerName(1)] = struct{}{}
}
common.Helper()方法标记当前函数为help
函数,所谓help
函数,即其中打印的日志,不记录help
函数的函数名及行号,而是记录上一层函数的函数名和行号。
赠人玫瑰手留余香,如果觉得不错请给个赞~
本篇文章已归档到GitHub项目,求星~ 点我即达
转载于:https://my.oschina.net/renhc/blog/3008988
Go testing.common公共类源码剖析相关推荐
- Thread类源码剖析
目录 1.引子 2.JVM线程状态 3.Thread常用方法 4.拓展点 一.引子 说来也有些汗颜,搞了几年java,忽然发现竟然没拜读过java.lang.Thread类源码,这次特地拿出来晒一晒. ...
- boost源码剖析之:泛型函数指针类boost::function(rev#3)
boost源码剖析之:泛型函数指针类boost::function(rev#3) 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba) Note: 并非新作,03年曾放 ...
- boost源码剖析之:泛型指针类any之海纳百川(rev#2)
boost源码剖析之:泛型指针类any之海纳百川(rev#2) 刘未鹏 C++的罗浮宫(http://blog.csdn.net/pongba) 动机 C++是强类型语言,所有强类型语言对类型的要求都 ...
- java.lang 源码剖析_java.lang.Void类源码解析
在一次源码查看ThreadGroup的时候,看到一段代码,为以下: /* * @throws NullPointerException if the parent argument is {@code ...
- 【知其然,知其所以然】配置中心 Apollo源码剖析
第2章 Apollo源码剖析 能力目标 能够基于Git导入Apollo源码 能够基于IDEA实现DEBUG分析APP创建 掌握Namespace创建过程 掌握Item创建过程 掌握灰度发布创建过程 1 ...
- Apollo 7.0——percception:lidar源码剖析(万字长文)
文章目录 组件启动 实现组件类 实现组件头文件 实现组件源文件 设置配置文件 启动组件 激光感知 目录结构 源码剖析 detection--init InitAlgorithmPlugin detec ...
- Xposed源码剖析——app_process作用详解
Xposed源码剖析--app_process作用详解 首先吐槽一下CSDN的改版吧,发表这篇文章之前其实我已经将此篇文章写过了两三次了.就是发表不成功.而且CSDN将我的文章草稿也一带>删除掉 ...
- tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析
[0]README 0.0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(11)StandardWrapper源码剖析" ...
- pymavlink 源码剖析(一)之XML文件的数据解析
文章目录 1 引言 2 pymavlink 的代码自动生成方法 3 XML 文件的数据解析 3.1 XML 文件预处理 3.2 解析 XML 的数据 3.2.1 依据协议版本初始化一些版本特征变量 3 ...
最新文章
- openssl 自建CA签发证书 网站https的ssl通信
- 通过UltraISO来提取U盘启动盘的ISO镜像文件
- 1分钟快速生成用于网页内容提取的xslt
- python 递归 分叉_浅谈Python 递归算法指归
- 安卓入门系列-03安卓的开发方式(逻辑与视图分离)
- 2021年考计算机考研三战,考研越来越难,2021考研人将会面临哪三大挑战?
- 使用SQL Server Management Studio 创建作业备份数据库
- HTTP协议的无状态性
- mysql一般要配置的几个小节及选项是_MySQL - 必知必会(下)
- 软件学报 流程 期刊投稿记录 状态变更 时间
- 二级c语言考试系统安卓,二级C语言考试系统
- QT实现操控打印机打印图片
- B站4K视频下载方法
- php sqlserver 日期转字符串,sqlserver 时间(datetime)转换成字符串
- php如何采集,php采集入门教程,教你如何写采集
- The following packages will be SUPERCEDED by a higher-priority channel是什么意思?
- メリッサ / 梅莉莎
- 李宏毅2022机器学习HW2解析
- Jquery图片轮播(连续滚动+突出显示)
- iOS:quartz2D绘图(绘制渐变图形)
热门文章
- 斯坦福学者让太阳能电池在夜间发电,功率可达50毫瓦/平方米
- VNote中公式插件MathJax不显示换行解决
- 2013年6月22日全国高校计算机联合考试广西考区一级笔试试题,全国高校计算机联合考试(广西考区)一级笔试试题卷2010年6月26日A.doc...
- 计算机技术在口腔医学中的应用研究,人工智能在口腔医学中的应用进展
- Html A标签中 href 和 onclick用法、区别、优先级别
- java中onclick的用法,使用jQuery修改onclick函数
- 高手不用Redis内存数据库一
- 林大OJ习题 2020年1月14日
- 无法实名支付宝余额如何转出?除了爱心捐款有别的办法吗?
- 淘宝充值150,被骗惨了…呜呜呜…