go test
-
Go语言中的测试依赖
go test
命令。 -
go test
命令是用来运行测试代码的工具。测试代码通常与要测试的代码放在同一个包中,并且测试文件通常以_test.go
结尾。 -
go test
命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go
为后缀名的源代码文件都是go test
测试的一部分,不会被go build
编译到最终的可执行文件中。 -
测试文件通常与被测试的代码文件命名相同,但以
_test.go
结尾。例如,如果你有一个名为math.go
的文件,其测试文件应该命名为math_test.go
。
源文件: math.go
测试文件:math_test.go
- 我们执行
go test
命令时,它会遍历该go包中所有以_test.go
结尾的测试文件, 然后调用并执行测试文件中符合go test
规则的函数帮助我们实现自动化测试。 go test
测试过程为生成1个临时的main包用于调用相应的测试函数,然后构建并运行测试文件中的函数、报告测试结果,最后清理测试中生成的临时文件。
测试代码结构
在*_test.go
文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数
类型 | 格式 | 作用 |
---|---|---|
测试函数 | 函数名前缀为Test | 测试程序的一些逻辑行为是否正确 |
基准函数 | 函数名前缀为Benchmark | 测试函数的性能 |
示例函数 | 函数名前缀为Example | 为文档提供示例文档 |
单元测试
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
- 名称以
Test
开头。 - 接受一个
*testing.T
参数。 - 没有返回值。
func TestAdd(t *testing.T) {
// 测试逻辑
}
其中参数t
用于报告测试失败和附加的日志信息。 testing.T
的拥有的方法如下
func (c *T) Cleanup(func()) //注册一个清理函数,该函数将在测试函数结束时执行
func (c *T) Error(args ...interface{})//记录一个错误,但不立即停止测试。它相当于 Log 后跟 Fail
func (c *T) Errorf(format string, args ...interface{}) //使用格式化的字符串记录一个错误,但不立即停止测试
func (c *T) Fail() //标记当前测试为失败,但不会停止测试函数的执行
func (c *T) FailNow() //立即标记当前测试为失败,并停止测试函数的执行
func (c *T) Failed() bool //返回一个布尔值,指示当前测试是否失败
func (c *T) Fatal(args ...interface{})//记录一个错误,并立即停止测试函数的执行
func (c *T) Fatalf(format string, args ...interface{}) //使用格式化的字符串记录一个错误,并立即停止测试函数的执行
func (c *T) Helper() //标记当前函数为测试辅助函数。如果测试失败,堆栈跟踪将从辅助函数开始,而不是从测试函数开始
func (c *T) Log(args ...interface{}) //记录一条日志信息,通常用于调试
func (c *T) Logf(format string, args ...interface{}) //使用格式化的字符串记录一条日志信息
func (c *T) Name() string //返回当前测试或基准测试的名称
func (c *T) Skip(args ...interface{}) //跳过当前测试,并记录跳过的原因
func (c *T) SkipNow() //立即跳过当前测试,并停止测试函数的执行
func (c *T) Skipf(format string, args ...interface{}) //使用格式化的字符串跳过当前测试,并记录跳过的原因
func (c *T) Skipped() bool //返回一个布尔值,指示当前测试是否被跳过
func (c *T) TempDir() string //返回一个临时目录的路径,该目录在测试期间存在,并在测试结束时被删除。
基准测试
测试程序执行时间复杂度、空间复杂度
- 名称以
Benchmark
开头。 - 接受一个
*testing.B
参数。 - 没有返回值。
func BenchmarkAdd(b *testing.B) {
// 基准测试逻辑
}
示例测试
为调用该功能代码的人提供演示
- 名称以
Example
开头。 - 没有参数和返回值。
- 通常包含一个
Output:
注释,表示期望的输出。
func ExampleAdd() {
// 示例代码
// Output: 期望的输出
}
测试执行
-v
:详细输出。-run
:运行匹配特定模式的测试函数。-cover
:输出测试覆盖率。-bench
:运行基准测试。-count
:运行测试和基准测试的次数。
# 匹配当前目录下*_test.go命令的文件,执行每一个测试函数
go test -v
# 执行 calc_test.go 文件中的所有测试函数,结尾需要标注原函数
go test -v calc_test.go calc.go
# 指定特定的测试函数(其中:-count=1用于跳过缓存)
go test -v -run TestAdd calc_test.go calc.go -count=1
# 执行基准测试
go test -benchmem -bench
testing.T
func (c *T) Cleanup(f func())
参数
f func()
: 一个无参数无返回值的函数,它将在测试函数结束时执行
功能
- 注册一个清理函数
f
,该函数将在当前测试函数结束时自动调用 - 如果测试函数因为
FailNow()
、SkipNow()
或 panic 而提前退出,清理函数仍然会被调用
示例
package mypackage
import (
"io/ioutil"
"os"
"testing"
)
func TestCreateAndCleanupFile(t *testing.T) {
// 创建一个临时文件
tempFile, err := ioutil.TempFile("", "example")
if err != nil {
t.Fatalf("Failed to create temp file: %v", err)
}
// 注册清理函数,确保在测试结束后删除临时文件
t.Cleanup(func() {
tempFile.Close()
os.Remove(tempFile.Name())
})
// 使用临时文件进行测试
// ...
}
- 无论测试成功还是失败,
Cleanup(func())
确保了tempFile.Close()
和os.Remove(tempFile.Name())
会被调用,从而清理了创建的临时文件
注意事项
- 测试函数执行完毕,没有调用
FailNow()
、SkipNow()
或遇到panic,那么注册的清理函数将在测试函数返回之后按注册的逆序执行 - 测试函数调用了
FailNow()
或遇到了panic,导致测试提前终止,那么注册的清理函数仍然会在测试终止之前按注册的逆序执行 - 测试函数调用了
SkipNow()
或Skipf()
并被跳过,清理函数同样会在跳过操作之后按注册的逆序执行
func (c *T) Error(args …interface{})
参数
args ...interface{}
: 可以是任意类型的参数,通常是一个格式化的字符串,用于描述测试失败的原因。
功能
- 记录错误信息。
- 标记测试为失败。
- 继续执行测试函数。
示例
package main
import (
"testing"
)
// 计算两个整数的和
func add(a, b int) int {
// 返回两数之和
return a + b
}
// 测试add函数的正确性
func TestAdd(t *testing.T) {
// 检查正数相加的情况
if add(1, 2) != 3 {
t.Error("两个正数相加的测试失败")
}
// 检查负数相加的情况
if add(-1, -2) != -3 {
t.Error("两个负数相加的测试失败")
}
}
- 如果 Add 函数返回的值与预期不符,t.Error 将被调用以报告错误。测试将继续执行到结束,然后测试框架会报告所有由 Error 方法记录的失败
func (c *****T) Errorf(format string, args **…**interface{})
参数
format string
: 一个格式化字符串,用于指定错误消息的格式args ...interface{}
: 可变数量的参数,它们将被格式化并添加到错误消息中
功能
- 使用格式化字符串记录错误信息
- 标记测试为失败
- 继续执行测试函数
示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Errorf("Add(1, 2) = %d; want 3", sum)
}
}
func Add(a, b int) int {
return a + b
}
- 如果
Add
函数的实现不正确,TestAdd
测试函数会使用t.Errorf
方法记录一个格式化的错误消息,并标记测试为失败。测试函数会继续执行,直到FailNow()
被调用或测试函数的自然结束 - 使用
Errorf(format string, args ...interface{})
方法,可以更清晰地报告错误信息,使得测试失败的原因更加直观
func (c *****T) Fail()
功能
- 标记测试为失败。
- 继续执行测试函数。
示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
}
func Add(a, b int) int {
return a + b
}
-
如果
Add
函数的实现不正确,TestAdd
测试函数会使用t.Fail()
方法标记测试为失败。测试函数会继续执行,直到FailNow()
被调用或测试函数的自然结束 -
使用
Fail()
方法,可以在测试失败时提供一个简单的标记,而无需记录详细的错误信息。如果需要记录详细的错误信息,可以使用Errorf(format string, args ...interface{})
或Error(args ...interface{})
方法 -
在 Go 语言的
testing
包中,FailNow()
方法是一个用于立即标记测试失败并停止执行的方法。当测试代码发现一个严重错误时,可以通过调用FailNow()
来记录错误,并立即停止测试的执行
func (c *T) FailNow()
语法
func (c *T) FailNow()
功能
- 标记测试为失败。
- 立即停止测试函数的执行。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.FailNow()
}
}
func Add(a, b int) int {
return a + b
}
- 在这个例子中,如果
Add
函数的实现不正确,TestAdd
测试函数会使用t.FailNow()
方法标记测试为失败,并立即停止测试的执行。这意味着测试函数中的任何后续代码都不会被执行 - 使用
FailNow()
方法,可以在遇到严重问题时立即停止测试,避免执行可能会产生更多问题的代码。这种方法通常用于处理测试代码中无法继续执行的情况,例如当依赖的服务不可用或关键资源未正确设置时
func (c *T) Failed() bool
返回值
- 一个布尔值,如果测试失败,返回
true
;否则返回false
。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
// 检查测试是否失败
if t.Failed() {
// 处理测试失败的逻辑
}
}
func Add(a, b int) int {
return a + b
}
-
如果
Add
函数的实现不正确,TestAdd
测试函数会使用t.Fail()
方法标记测试为失败。之后,测试函数会调用t.Failed()
来检查测试是否失败,并根据结果执行相应的逻辑。 -
使用
Failed()
方法,可以在测试代码中检查测试是否已经失败,并根据测试状态执行不同的逻辑。这有助于在测试失败后进行额外的检查或清理操作。 -
在 Go 语言的
testing
包中,Fatal(args ...interface{})
方法是一个用于报告测试失败并立即停止执行的方法。当测试代码遇到一个无法恢复的错误时,可以通过调用Fatal(args ...interface{})
来记录错误信息,并立即停止测试的执行
func (c *T) Fatal(args …interface{})
参数
args ...interface{}
: 可以是任意类型的参数,通常是一个格式化的字符串,用于描述测试失败的原因。
功能
- 记录错误信息。
- 标记测试为失败。
- 立即停止测试函数的执行。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fatal("Add(1, 2) failed")
}
}
func Add(a, b int) int {
return a + b
}
- 在这个例子中,如果
Add
函数的实现不正确,TestAdd
测试函数会使用t.Fatal
方法记录一个错误消息,并立即停止测试的执行 - 使用
Fatal(args ...interface{})
方法,可以在遇到无法继续测试的情况时立即停止测试,避免执行可能会产生更多问题的代码。这种方法通常用于处理测试代码中无法恢复的严重错误,例如当关键的依赖服务不可用时
func (c *****T) Fatalf(format string, args **…**interface{})
参数
format string
: 一个格式化字符串,用于指定错误消息的格式args ...interface{}
: 可变数量的参数,它们将被格式化并添加到错误消息中
功能
- 使用格式化字符串记录错误信息
- 标记测试为失败
- 立即停止测试函数的执行
示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fatalf("Add(1, 2) failed with unexpected result: %d", sum)
}
}
func Add(a, b int) int {
return a + b
}
- 如果
Add
函数的实现不正确,TestAdd
测试函数会使用t.Fatalf
方法记录一个格式化的错误消息,并立即停止测试的执行。测试函数中的任何后续代码都不会被执行 - 使用
Fatalf(format string, args ...interface{})
方法,可以更清晰地报告错误信息,使得测试失败的原因更加直观。当遇到无法继续测试的情况时,Fatalf
是一个非常有用的工具
func (c *****T) Helper()
功能
- 标记当前函数为测试辅助函数。
- 如果测试失败,堆栈跟踪将从辅助函数开始。
示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
// 标记当前函数为辅助函数
t.Helper()
// 辅助函数代码
}
func Add(a, b int) int {
return a + b
}
TestAdd
测试函数调用了t.Helper()
,这意味着如果测试失败,堆栈跟踪将从辅助函数开始,而不是从测试函数开始。这有助于开发者更清晰地理解失败的位置,尤其是在使用嵌套的测试函数时。- 使用
Helper()
方法,可以在测试函数中定义辅助函数,这些辅助函数可以包含在测试失败时需要执行的代码,如清理操作。通过标记辅助函数,可以避免在测试失败时产生冗长的堆栈跟踪,从而使调试更加容易。
func (c *T) Log(args …interface{})
参数
args ...interface{}
: 可以是任意类型的参数,通常是一个格式化的字符串,用于描述日志信息。
功能
- 记录日志信息。
- 不会停止测试的执行。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
// 记录一些日志信息
t.Log("Add(1, 2) returned", sum)
}
func Add(a, b int) int {
return a + b
}
- 如果
Add
函数的实现不正确,TestAdd
测试函数会使用t.Fail()
方法标记测试为失败。之后,测试函数会调用t.Log
方法来记录一些日志信息,这些信息会被输出到测试运行器的标准输出中。 - 使用
Log(args ...interface{})
方法,可以在测试过程中记录调试信息或操作步骤,这些信息有助于开发者理解和调试测试代码。
func (c *****T) Logf(format string, args **…**interface{})
参数
format string
: 一个格式化字符串,用于指定日志消息的格式。args ...interface{}
: 可变数量的参数,它们将被格式化并添加到日志消息中。
功能
- 使用格式化字符串记录日志信息。
- 不会停止测试的执行。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
// 记录一些格式化的日志信息
t.Logf("Add(1, 2) returned: %d", sum)
}
func Add(a, b int) int {
return a + b
}
- 如果
Add
函数的实现不正确,TestAdd
测试函数会使用t.Fail()
方法标记测试为失败。之后,测试函数会调用t.Logf
方法来记录一些格式化的日志信息,这些信息会被输出到测试运行器的标准输出中。 - 使用
Logf(format string, args ...interface{})
方法,可以更清晰地记录调试信息或操作步骤,使得日志信息更加易读和易于理解。这有助于开发者理解和调试测试代码。
func (c *T) Name() string
返回值
- 一个字符串,表示当前测试或基准测试的名称。
示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
}
func Add(a, b int) int {
return a + b
}
TestAdd
测试函数会调用t.Name()
来获取当前测试的名称。这个名称是在测试函数执行之前由测试运行器指定的,通常与测试文件的名称和测试函数的名称相关联。- 使用
Name()
方法,可以在测试代码中根据测试的名称执行特定的逻辑,例如,根据测试的名称选择性地运行某些测试。这有助于在测试框架中实现更复杂的测试逻辑。
func (c *****T) Skip(args **…**interface{})
在 Go 语言的 testing
包中,Skip(args ...interface{})
方法是一个用于跳过当前测试的方法。当测试代码遇到一个条件,使得当前测试不应该被执行时,可以通过调用 Skip(args ...interface{})
来跳过当前测试。
语法
func (c *T) Skip(args ...interface{})
参数
args ...interface{}
: 可以是任意类型的参数,通常是一个格式化的字符串,用于描述跳过测试的原因。
功能
- 跳过当前测试。
- 记录跳过测试的原因。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
// 如果条件不满足,跳过测试
if condition {
t.Skip("Skipping test because condition is not met")
}
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
}
func Add(a, b int) int {
return a + b
}
在这个例子中,如果 condition
为 true
,TestAdd
测试函数会使用 t.Skip
方法跳过当前测试,并记录跳过测试的原因。这意味着测试函数中的任何后续代码都不会被执行。
使用 Skip(args ...interface{})
方法,可以在测试代码中根据特定条件跳过测试,例如,当依赖的服务不可用或关键资源未正确设置时。这有助于确保测试的可重复性和可靠性。
func (c *****T) SkipNow()
在 Go 语言的 testing
包中,SkipNow()
方法是一个用于立即跳过当前测试并停止执行的方法。当测试代码遇到一个条件,使得当前测试不应该被执行时,可以通过调用 SkipNow()
来立即跳过当前测试。
语法
func (c *T) SkipNow()
功能
- 立即跳过当前测试。
- 停止测试函数的执行。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
// 如果条件不满足,立即跳过测试
if condition {
t.SkipNow()
}
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
}
func Add(a, b int) int {
return a + b
}
在这个例子中,如果 condition
为 true
,TestAdd
测试函数会使用 t.SkipNow
方法立即跳过当前测试,并停止测试的执行。这意味着测试函数中的任何后续代码都不会被执行。
使用 SkipNow()
方法,可以在测试代码中根据特定条件立即停止测试,避免执行可能会产生更多问题的代码。这种方法通常用于处理测试代码中无法继续执行的情况,例如当关键的依赖服务不可用时。
func (c *****T) Skipf(format string, args **…**interface{})
在 Go 语言的 testing
包中,Skipf(format string, args ...interface{})
方法是一个用于记录跳过测试原因并立即停止执行的方法。当测试代码遇到一个条件,使得当前测试不应该被执行时,可以通过调用 Skipf(format string, args ...interface{})
来记录跳过测试的原因,并立即停止测试的执行。
语法
func (c *T) Skipf(format string, args ...interface{})
参数
format string
: 一个格式化字符串,用于指定跳过测试原因的格式。args ...interface{}
: 可变数量的参数,它们将被格式化并添加到跳过测试原因中。
功能
- 使用格式化字符串记录跳过测试的原因。
- 立即停止测试函数的执行。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
// 如果条件不满足,记录跳过测试的原因并立即停止执行
if condition {
t.Skipf("Skipping test because condition is not met: %s", reason)
}
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
}
func Add(a, b int) int {
return a + b
}
在这个例子中,如果 condition
为 true
,TestAdd
测试函数会使用 t.Skipf
方法记录一个格式化的跳过测试原因,并立即停止测试的执行。这意味着测试函数中的任何后续代码都不会被执行。
使用 Skipf(format string, args ...interface{})
方法,可以更清晰地记录跳过测试的原因,使得测试跳过的情况更加直观。当遇到无法继续测试的情况时,Skipf
是一个非常有用的工具。
func (c *****T) Skipped() bool
在 Go 语言的 testing
包中,Skipped() bool
方法是一个用于检查当前测试是否被跳过的辅助函数。通过调用 Skipped()
,可以检查测试是否在运行过程中被标记为跳过。
语法
func (c *T) Skipped() bool
返回值
- 一个布尔值,如果测试被跳过,返回
true
;否则返回false
。
使用示例
package mypackage
import (
"testing"
)
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum != 3 {
t.Fail()
}
// 检查测试是否被跳过
if t.Skipped() {
// 处理测试被跳过的逻辑
}
}
func Add(a, b int) int {
return a + b
}
在这个例子中,如果 Add
函数的实现不正确,TestAdd
测试函数会使用 t.Fail()
方法标记测试为失败。之后,测试函数会调用 t.Skipped()
来检查测试是否被跳过,并根据结果执行相应的逻辑。
使用 Skipped()
方法,可以在测试代码中检查测试是否已经被跳过,并根据测试状态执行不同的逻辑。这有助于在测试被跳过时进行额外的检查或清理操作。
func (c *T) TempDir() string
返回值
- 一个字符串,表示当前测试的临时目录路径。
使用示例
package mypackage
import (
"os"
"testing"
)
func TestFileOperations(t *testing.T) {
// 获取当前测试的临时目录
tempDir := t.TempDir()
// 在临时目录中创建一个文件
file, err := os.Create(filepath.Join(tempDir, "temp_file.txt"))
if err != nil {
t.Fatal(err)
}
defer file.Close()
// 在临时目录中执行其他操作
// ...
}
在这个例子中,TestFileOperations
测试函数会调用 t.TempDir()
方法来获取一个唯一的临时目录路径。之后,测试函数会在这个临时目录中创建一个文件,并在执行其他操作时使用这个临时目录。
使用 TempDir()
方法,可以在测试中安全地创建临时文件和目录,这些文件和目录在测试结束后会被自动删除,从而避免了清理工作。这有助于简化测试代码,并确保测试的可重复性。