Go语言与数据库开发:01-10

测试

现在的程序已经远比Wilkes时代的更大也更复杂,也有许多技术可以让软件的复杂性可得到
控制。其中有两种技术在实践中证明是比较有效的。第一种是代码在被正式部署前需要进行
代码评审。第二种则是测试

我们说测试的时候一般是指自动化测试,也就是写一些小的程序用来检测被测试代码(产品
代码)的行为和预期的一样,这些通常都是精心设计的执行某些特定的功能或者是通过随机
性的输入要验证边界的处理。

软件测试是一个巨大的领域。测试的任务可能已经占据了一些程序员的部分时间和另一些程
序员的全部时间。和软件测试技术相关的图书或博客文章有成千上万之多。对于每一种主流
的编程语言,都会有一打的用于测试的软件包,同时也有大量的测试相关的理论,而且每种
都吸引了大量技术先驱和追随者。这些都足以说服那些想要编写有效测试的程序员重新学习
一套全新的技能。

Go语言的测试技术是相对低级的。它依赖一个go test测试命令和一组按照约定方式编写的测
试函数,测试命令可以运行这些测试函数。编写相对轻量级的纯测试代码是有效的,而且它
很容易延伸到基准测试和示例文档。

在实践中,编写测试代码和编写程序本身并没有多大区别。我们编写的每一个函数也是针对
每个具体的任务。我们必须小心处理边界条件,思考合适的数据结构,推断合适的输入应该
产生什么样的结果输出。编程测试代码和编写普通的Go代码过程是类似的;它并不需要学习
新的符号、规则和工具。

go test

go test命令是一个按照一定的约定和组织的测试代码的驱动程序。在包目录内,所有以
_test.go为后缀名的源文件并不是go build构建包的一部分,它们是go test测试的一部分。
在*_test.go文件中,有三种类型的函数:测试函数、基准测试函数、示例函数。一个测试函
数是以Test为函数名前缀的函数,用于测试程序的一些逻辑行为是否正确;go test命令会调用
这些测试函数并报告测试结果是PASS或FAIL。基准测试函数是以Benchmark为函数名前缀的
函数,它们用于衡量一些函数的性能;go test命令会多次运行基准函数以计算一个平均的执
行时间。

go test命令会遍历所有的*_test.go文件中符合上述命名规则的函数,然后生成一个临时的
main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的
临时文件。

测试函数

每个测试函数必须导入testing包。测试函数有如下的签名:
func TestName(t *testing.T) {
// ...
}
测试函数的名字必须以Test开头,可选的后缀名必须以大写字母开头:

func TestSin(t testing.T) { / ... */ }
func TestCos(t testing.T) { / ... */ }
func TestLog(t testing.T) { / ... */ }
其中t参数用于报告测试失败和附加的日志信息。

go test 命令如果没有参数指定包那么将默认采用当前目录对应的包(和 go build 命令一
样)。我们可以用下面的命令构建和运行测试。
$ cd $GOPATH/src/gopl.io/ch11/word1
$ go test
ok gopl.io/ch11/word1 0.008s

先编写测试用例并观察到测试用例触发了和用户报告的错误相同的描述是一个好的测试习
惯。只有这样,我们才能定位我们要真正解决的问题。

先写测试用例的另外的好处是,运行测试通常会比手工描述报告的处理更快,这让我们可以
进行快速地迭代。如果测试集有很多运行缓慢的测试,我们可以通过只选择运行某些特定的
测试来加快测试速度。

当然,一旦我们已经修复了失败的测试用例,在我们提交代码更新之前,我们应该以不带参
数的 go test 命令运行全部的测试用例,以确保修复失败测试的同时没有引入新的问题。

失败测试的输出并不包括调用t.Errorf时刻的堆栈调用信息。和其他编程语言或测试框架的
assert断言不同,t.Errorf调用也没有引起panic异常或停止测试的执行。即使表格中前面的数
据导致了测试的失败,表格后面的测试数据依然会运行测试,因此在一个测试中我们可能了
解多个失败的信息。

如果我们真的需要停止测试,或许是因为初始化失败或可能是早先的错误导致了后续错误等
原因,我们可以使用t.Fatal或t.Fatalf停止当前测试函数。它们必须在和测试函数同一个
goroutine内调用。

测试失败的信息一般的形式是“f(x) = y, want z”,其中f(x)解释了失败的操作和对应的输出,y
是实际的运行结果,z是期望的正确的结果。就像前面检查回文字符串的例子,实际的函数用
于f(x)部分。

如果显示x是表格驱动型测试中比较重要的部分,因为同一个断言可能对应不同的表格项执行多次。
要避免无用和冗余的信息。在测试类似IsPalindrome返回布尔类型的函数时,可以忽略并没有额外
信息的z部分。如果x、y或z是y的长度,输出一个相关部分的简明总结即可。测试的作者应该要努力
帮助程序员诊断测试失败的原因。

时间: 2024-10-26 02:44:13

Go语言与数据库开发:01-10的相关文章

Go语言与数据库开发:01-01

一.前言 Google的三位大牛,为了解决在21世纪多核和网络化环境下越来越复杂的编程问题而发明了go语言, 从2007年9月开始设计和实现,于2009年的11月对外正式发布.从版本的发布历史来看,go语言是从 Ken Thompson发明的B语言.Dennis M. Ritchie发明的C语言逐步演化过来的,是C语言家族的成员, 因此很多人将Go语言称为21世纪的C语言. 纵观这几年来的发展趋势,Go语言已经成为云计算.云存储时代最重要的基础编程语言. Go语言有着和C语言类似的语法,但是它不

Go语言与数据库开发:01-08

基于共享变量的并发 .竞争条件 在一个线性(就是说只有一个goroutine的)的程序中,程序的执行顺序只由程序的逻辑来决定. 例如,我们有一段语句序列,第一个在第二个之前(废话),以此类推.在有两个或更多 goroutine的程序中,每一个goroutine内的语句也是按照既定的顺序去执行的,但是一般情况 下我们没法去知道分别位于两个goroutine的事件x和y的执行顺序,x是在y之前还是之后还是 同时发生是没法判断的.当我们能够没有办法自信地确认一个事件是在另一个事件的前面或 者后面发生的

Go语言与数据库开发:01-06

Go语言包含了对OOP语言的支持,接下来我们来看看Go语言中的方法. 尽管没有被大众所接受的明确的OOP的定义,从我们的理解来讲,一个对象其实也就是一个 简单的值或者一个变量,在这个对象中会包含一些方法,而一个方法则是一个一个和特殊类 型关联的函数.一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个对 象的用户就不需要直接去操作对象,而是借助方法来做这些事情. 在函数声明时,在其名字之前放上一个变量,即是一个方法. 这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了

Go语言与数据库开发:01-11

反射 Go语言提供了一种机制在运行时更新变量和检查它们的值.调用它们的方法和它们支持的内在操作,但是在编译时并不知道这些变量的具体类型.这种机制被称为反射.反射也可以让我们将类型本身作为第一类的值类型处理. Go语言的反射特性,看看它可以给语言增加哪些表达力,以及在两个至关重要的API是如何用反射机制的:一个是fmt包提供的字符串格式功能,另一个是类似encoding/json和encoding/xml提供的针对特定协议的编解码功能. 反射是一个复杂的内省技术,不应该随意使用,因此,尽管上面这些

Go语言与数据库开发:01-09

包和工具 Go语言有超过100个的标准包(译注:可以用 go list std | wc -l 命令查看标准包的具体数 目),标准库为大多数的程序提供了必要的基础构件.在Go的社区,有很多成熟的包被设 计.共享.重用和改进,目前互联网上已经发布了非常多的Go语音开源包,它们可以通过http://godoc.org 检索. Go还自带了工具箱,里面有很多用来简化工作区和包管理的小工具. 包简介 任何包系统设计的目的都是为了简化大型程序的设计和维护工作,通过将一组相关的特性放 进一个独立的单元以便于

Go语言与数据库开发:01-04

讲完基础数据类型之后,我们接着学习复合数据类型. 复合数据类型,它是以不同的方式组合基本类型可以构造出来的复合数据类型. 几个基本的复合数据类型: 数组,是由同构的元素组成--每个数组元素都是完全相同的类型; 结构体,则是由异构的元素组成的; 数组和结构体都是有固定内存大小的数据结构; slice和map则是动态的数据结构,它们将根据需要动态增长: 数组: 数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成. 因为数组的长度是固定的,因此在Go语言中很少直接使用数组.

Go语言与数据库开发:01-02

接下来,开始了解go语言的程序结构,基础要打牢. Go语言和其他编程语言一样,一个大的程序是由很多小的基础构件组成的.变量保存值,简 单的加法和减法运算被组合成较复杂的表达式.基础类型被聚合为数组或结构体等更复杂的 数据结构.然后使用if和for之类的控制语句来组织和控制表达式的执行流程.然后多个语句被 组织到一个个函数中,以便代码的隔离和复用.函数以源文件和包的方式被组织. . 关于命名: 在Go中是区分大小写的:关键字不能用于自定义名字: Go语言的风格是尽量使用短小的名字,对于局部变量尤其

Go语言与数据库开发:01-07

在本节,我们来说一下并发. 并发程序指同时进行多个任务的程序,随着硬件的发展,并发程序变得越来越重要 Go语言中的并发程序可以用两种手段来实现.尽管Go对并发的支持是众多强力特性之一,但跟踪调试并发程序还是很困难,在线性程序中 形成的直觉往往还会使我们误入歧途. . Goroutines 在Go语言中,每一个并发的执行单元叫作一个goroutine.设想这里的一个程序有两个函数, 一个函数做计算,另一个输出结果,假设两个函数没有相互之间的调用关系.一个线性的程 序会先调用其中的一个函数,然后再调

Go语言与数据库开发:01-03

在本文中,我们将介绍go的基础数据类型. 虽然从底层而言,所有的数据都是由比特组成,但计算机一般操作的是固定大小的数,如整 数.浮点数.比特数组.内存地址等.进一步将这些数组织在一起,就可表达更多的对象, 例如数据包.像素点.诗歌,甚至其他任何对象.Go语言提供了丰富的数据组织形式,这依 赖于Go语言内置的数据类型.这些内置的数据类型,兼顾了硬件的特性和表达复杂数据结构 的便捷性. Go语言将数据类型分为四类:基础类型.复合类型.引用类型和接口类型. 本文将介绍基础类型,包括:数字.字符串和布尔