当前位置: 首页 > news >正文

Go:测试

go test 工具

go test是 Go 语言包的测试驱动程序 ,包依据特定约定组织 。包目录中以_test.go结尾的文件是go test编译对象,而非go build的编译目标 。

特殊测试函数

*_test.go文件中有三种特殊函数 :

  • 功能测试函数:以Test为前缀命名 ,用于检测程序逻辑正确性 ,go test运行后报告测试结果为PASS(通过)或FAIL(失败 )。
  • 基准测试函数:名称以Benchmark开头 ,用于测试某些操作性能 ,go test会汇报操作的平均执行时间 。
  • 示例函数:名称以Example开头 ,提供经机器检查的文档 。后续 11.2 节、11.4 节、11.6 节将分别详述这三种函数。

工作流程

go test工具扫描*_test.go文件查找上述特殊函数 ,生成临时main包调用它们 ,接着进行编译、运行并汇报结果 ,最后清空临时文件 。

Test 函数

func TestName(t *testing.T) {//...
}

测试文件需导入testing包 。功能测试函数以Test为前缀,可选后缀以大写字母开头,函数签名为func TestName(t *testing.T) ,参数t用于汇报测试失败和记录日志 。

package word
func IsPalindrome(s string) bool {for i := range s {if s[i]!= s[len(s)-1-i] {return false}}return true
}package word_test
import "testing"
func TestPalindrome(t *testing.T) {if!IsPalindrome("detartrated") {t.Error(`IsPalindrome("detartrated") = false`)}if!IsPalindrome("kayak") {t.Error(`IsPalindrome("kayak") = false`)}
}
func TestNonPalindrome(t *testing.T) {if IsPalindrome("palindrome") {t.Error(`IsPalindrome("palindrome") = true`)}
}

IsPalindrome函数用于判断字符串是否为回文 。在word_test.go文件中,有TestPalindromeTestNonPalindrome两个测试函数,分别检查IsPalindrome对回文和非回文输入的判断是否正确,通过t.Error报告错误 。

测试运行与反馈

go test(或go build )在不指定包参数时,以当前目录所在包为参数 。运行测试命令后,可得到测试结果,如测试通过显示ok及耗时;测试失败会给出具体错误信息,像新增TestFrenchPalindromeTestCanalPalindrome测试函数后,因IsPalindrome函数存在问题导致测试失败 。

测试选项

  • -v选项:可输出包中每个测试用例的名称和执行时间,便于详细了解测试过程 。
  • -run选项:参数为正则表达式,能让go test只运行函数名称匹配给定模式的测试函数 。

随机测试

基于表的测试针对精心挑选的输入检测函数,而随机测试通过构建随机输入扩展测试覆盖范围 。

随机测试策略

  • 低效算法对比:额外编写一个使用低效但清晰算法的函数,对比两种实现的输出是否一致 。
  • 模式构建输入:构建符合特定模式的输入,从而预知对应输出 。

注意事项

随机测试具有不确定性,测试用例失败时要记录足够信息以便重现问题 。对于复杂输入函数,记录伪随机数生成器种子比存储整个输入数据结构更简便 。使用当前时间作种子源,在自动化系统周期性运行测试时,每次运行都会得到新输入 。

测试命令

go test工具不仅用于测试库代码包,也可测试命令 。包名main一般生成可执行文件,但也能当作库导入进行测试 。

测试代码编写

package mainimport ("bytes""fmt""testing"
)func TestEcho(t *testing.T) {var tests = []struct {newline boolsep     stringargs    []stringwant    string}{{true, "", []string{}, "\n"},{false, "", []string{}, ""},{true, "\t", []string{"one", "two", "three"}, "one\ttwo\tthree\n"},{true, ",", []string{"a", "b", "c"}, "a,b,c\n"},{false, ":", []string{"1", "2", "3"}, "1:2:3"},}for _, test := range tests {descr := fmt.Sprintf("echo(%v, %q, %q)",test.newline, test.sep, test.args)out = new(bytes.Buffer) // 捕获的输出if err := echo(test.newline, test.sep, test.args); err!= nil {t.Errorf("%s failed: %v", descr, err)continue}got := out.(*bytes.Buffer).String()if got!= test.want {t.Errorf("%s = %q, want %q", descr, got, test.want)}}
}

echo_test.go中编写测试代码 :

  • 导入相关包(bytesfmttesting ),定义TestEcho测试函数 。
  • 使用结构体数组tests组织测试用例,每个用例包含newlinesepargswant(预期输出 )字段 。
  • 遍历测试用例,为每个用例创建bytes.Buffer捕获输出,调用echo函数,若有错误用t.Errorf报告;若无错,对比实际输出与预期输出,不一致时也用t.Errorf报告 。

注意事项

  • 测试代码和产品代码在同一包(main包 )中,测试时该包当作库,TestEcho函数被测试驱动程序调用,main函数被忽略 。
  • 可通过添加测试用例扩展测试,文中展示添加错误预期结果的用例及测试失败输出 。
  • 测试代码中避免调用log.Fatalos.Exit ,以免阻止跟踪过程,预期错误应通过返回非空error值报告 。

白盒测试

测试分类可依据对测试包内部了解程度。黑盒测试仅通过公开 API 和文档了解包,包内部逻辑不透明;白盒测试可访问包内部函数和数据结构,进行常规用户无法做到的观察和改动,如检查数据类型不可变性维护情况 。二者互补,黑盒测试健壮,程序更新后基本无需修改,能发现 API 设计缺陷;白盒测试可对实现特定之处作更详细覆盖测试 。

  • TestIsPalindrome函数为例,仅调用导出的IsPalindrome函数,属于黑盒测试 。
  • TestEcho函数调用未导出的echo函数并更新未导出的全局变量out ,属于白盒测试 。

外部测试包

net/urlnet/http包为例,net/http依赖net/url ,若在net/url包内声明测试函数,可能导致包循环引用(Go 规范禁止 ) 。为解决此问题,引入外部测试包 。在net/url目录下,有url_test包声明的文件,后缀_testgo test单独编译该包并运行测试 。外部测试包无法通过常规路径导入 。

外部测试在单独包中,可引用依赖被测包的帮助包,这是包内测试无法做到的 。从设计层次看,外部测试包在其依赖的包之上,能更自由地导入其他包进行测试,尤其适合集成测试 。

  • go list工具:可汇总包目录中的文件类型 。
  • 特殊文件:有时外部测试包需特殊访问权限避免循环引用,会在包内测试文件(_test.go )添加函数声明,将包内部功能暴露给外部测试 ,仅为此目的且无自身测试的源文件一般叫export_test.go 。 。

编写有效测试

Go 测试框架极简,与其他语言测试框架不同 。其他语言框架借助反射或元数据识别测试函数,有测试 “启动” 和 “销毁” 钩子,提供断言、值比较、错误消息格式化等工具方法库 ,虽能让测试编写精细,但结果像用其他语言编写测试,且错误消息可能模糊、维护不友好 。

Go 期望测试编写者像编写普通程序一样,通过定义函数避免重复 。测试不是机械填表,测试过程要有良好用户界面(维护者即用户 ) 。好的测试在出错时不崩溃,能输出简洁清晰的现象描述及相关信息,助维护者定位问题;不应在首次失败时终止,而应尝试报告多个错误,从错误发生方式挖掘原因 。

避免脆弱的测试

如果程序在遇到新合法输入时常崩溃,说明程序有缺陷;若测试用例在程序可靠改动时奇怪失败,那么该测试用例是脆弱的 。最脆弱的测试在产品代码稍有改动(无论好坏 )就失败,这类测试被称为变化探测器或现状探测器 ,处理它们耗费的时间会抵消其带来的好处 。

当被测函数输出复杂(如长字符串、详细数据结构、文件等 )时,若在测试中检查输出完全匹配预期的 “幸运值” ,随着程序演进,输出内容或输入可能改变,导致测试失败 。比如输出部分内容可能向好改变,但因与测试预期不符而使测试不通过;复杂输入的函数也可能因测试输入不再合法而崩溃 。

避免脆弱测试的关键是仅检查关心的属性 。先测试程序中简单稳定的接口,再测内部函数 。设置断言时要有选择性,例如不追求字符串精确匹配,而是寻找程序进化中不变的子串 。还可编写稳定函数从复杂输出提取核心内容,使断言更可靠 。虽然前期需投入精力,但可减少修复奇怪失败测试的时间消耗。

覆盖率

测试旨在发现 bug 而非证明其不存在,再多测试也无法证明包无 bug ,但测试可增强对包在多种场景下可用的信心 。测试套件覆盖待测包的比例即测试覆盖率 ,它无法精确测量,因程序动态性,微小程序也难精准衡量,但可辅助将测试精力集中在关键处 。语句覆盖率是常用的简单方法,指部分语句在一次执行中至少执行一次 。

  • go test相关命令:检测测试可通过$ go test -v -run=Coverage gopl.io/ch7/eval ,加上-coverprofile=c.out标记运行测试,可启用覆盖数据收集,修改源代码副本,在语句块执行前设置布尔变量,执行结束将变量值写入c.out日志文件并输出执行语句汇总信息 。
  • go tool cover工具go tool cover可处理生成的日志,生成 HTML 报告 。如$ go tool cover -html=c.out ,在报告界面中,绿色(浅灰色 )标记的语句块表示被覆盖,红色(加阴影深灰色 )表示未被覆盖 。一元操作符Eval方法未被覆盖,添加新测试用例可使其被覆盖;panic语句未覆盖属正常,因不应执行到 。

Benchmark 函数

基准测试是在一定工作负载下检测程序性能的方法 。在 Go 中,基准测试函数名以Benchmark为前缀,拥有*testing.B参数,提供与性能检测相关方法,还包含整数型变量N ,用于指定被检测操作的执行次数 。

基准测试运行器开始时因不确定操作耗时,先用较小N值检测,再推断出足够大的N值以检测稳定运行时间 。在基准测试函数中实现循环,可在循环外执行必要初始化代码且不影响每次迭代时间 ,testing.B参数提供停止、恢复和重置计时器的方法 。

基准测试不仅告知给定操作绝对耗时,还用于比较不同操作相对耗时 。如处理不同数量元素耗时比较、I/O 缓冲区最佳大小选择、不同算法性能对比等 。性能比较函数可被多个Benchmark函数传入不同值调用 ,编写时注意不要用b.N作为输入大小,除非作为固定大小输入的循环次数 。基准测试在程序设计及演进过程中都有重要作用 。

性能剖析

基准测试对检测操作性能有帮助,但优化程序时,不能盲目过早优化 。多数情况下,过度关注微小性能提升是浪费时间,约 97% 场景中过早优化有害 。但也不能错过关键的 3%,需仔细排查关键代码,而不能仅凭直觉认定,性能剖析是发现关键代码的最佳技术 。

性能剖析通过自动化手段,基于程序执行中性能事件采样进行性能评测,生成性能剖析报告 。Go 支持多种性能剖析方式,每种与不同性能指标相关,且需记录相关事件及对应栈信息 :

  • CPU 性能剖析:识别执行过程中占用 CPU 多的函数 。CPU 上执行线程定时被操作系统中断,中断时记录性能剖析事件 。
  • 堆性能剖析:找出分配内存多的语句 。性能剖析库对内存分配调用采样,每个事件平均记录分配 512KB 内存 。
  • 阻塞性能剖析:识别阻塞协程久的操作,如系统调用、通道数据收发、获取锁等 。协程被阻塞时记录事件 。

获取与分析性能剖析报告

  • 获取报告:通过go test指定标记获取性能剖析报告,如-cpuprofile=cpu.out-blockprofile=block.out-memprofile=mem.out 。同时使用多个标记时,获取一种报告机制会覆盖其他类别报告 。非测试程序也可通过runtime API启用性能剖析 。
  • 分析报告:用pprof工具分析报告,通过go tool pprof间接使用 。基本用法需传入可执行文件和性能剖析日志 。因性能剖析日志不含函数名,需结合可执行文件理解数据 。go test启用性能剖析时会保存可执行文件并命名为foo.testfoo为被测包名 ) 。

Example 函数

func ExampleIsPalindrome() {fmt.Println(IsPalindrome("A man, a plan, a canal: Panama"))fmt.Println(IsPalindrome("palindrome"))// 输出 :// true// false
}

示例函数是go test特殊对待的函数,以Example开头 ,无参数无结果 。文中给出ExampleIsPalindrome示例函数,用于展示IsPalindrome函数功能,通过打印该函数对不同字符串的判断结果,并注释预期输出 。

示例函数目的

  • 作为文档:是描述库函数功能简洁直观的方式,可演示 API 中类型和函数交互 。基于Example函数后缀,godoc文档服务器可将其与所演示函数或包关联 ,如ExampleIsPalindromeIsPalindrome函数文档关联 。
  • 可执行测试:若示例函数最后含// 输出 :注释,go test运行时会执行该函数并检查输出与注释文本是否匹配 。
  • 提供手动实验代码godoc文档服务器利用 Go Playground 让用户在 Web 浏览器编辑运行示例函数,方便了解函数功能或语言特性 。

参考资料:《Go程序设计语言》

相关文章:

  • 强化学习的数学原理(十)actor-critic 方法
  • 接口测试(get请求方法)-----------实战演练
  • 得物golang一面
  • 在 Visual Studio Code 中安装通义灵码 - 智能编码助手
  • 【网络安全】谁入侵了我的调制解调器?(二)
  • NLP高频面试题(四十四)——RLHF过程中的马尔科夫决策过程及对话场景MDP设计
  • 【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——音频测试 #ES8388 #录音测试
  • AI Agent分类详解与对比
  • 力扣刷题Day 18:字符串解码(394)
  • 码界奇缘 Java 觉醒 后记 第二十五章 安全结界攻防战 - 从沙箱到模块化
  • 【SpringBoot】99、SpringBoot中整合RabbitMQ实现重试功能
  • 小白工具视频转wmv,支持多种格式视频在线转换为 WMV 格式,无需下载在线使用,方便快捷
  • 基于javaweb的SpringBoot兼职平台系统设计与实现(源码+文档+部署讲解)
  • 事务管理:确保数据一致性与业务完整性
  • celery rabbitmq 配置 broker和backend
  • 【充电器的原理】
  • CS5346 - Improving and Evaluating Effectiveness of Visualizations(提高和评估可视化的效果)
  • 云函数采集架构:Serverless模式下的动态IP与冷启动优化
  • 栅格数据处理
  • 技术速递|使用 BrowserStack App Automate 和 Appium UI 测试 .NET MAUI 应用
  • 巴中网站建设公司/百度收录怎么弄
  • 宿迁房产交易中心官网/免费seo工具大全
  • 企业酒店的网站建设/童程童美少儿编程怎样收费
  • 大良营销网站建设价位/天眼查企业查询入口
  • wordpress 子网站重命名/知识营销
  • 南宁网站建设技术支持/5g站长工具seo综合查询