Skip to content

在 Go 中,testing 包是标准库的一部分,用于编写和执行单元测试。它为编写、执行和组织测试提供了丰富的功能,包括断言、基准测试、示例测试等。

1. 单元测试

Go 的单元测试是基于函数和方法的,测试文件通常以 _test.go 结尾,测试函数以 Test 开头。

1.1. 编写单元测试

go
// calculator.go
package calculator

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

// calculator_test.go
package calculator

import "testing"

// 测试 Add 函数
func TestAdd(t *testing.T) {
    result := Add(1, 2)
    expected := 3

    if result != expected {
        t.Errorf("Expected %d, but got %d", expected, result)
    }
}
  • 测试函数名必须以 Test 开头,且接受一个 *testing.T 类型的参数。
  • 使用 t.Errorft.Fatal 来报告错误。

1.2. 运行单元测试

你可以使用 go test 命令来运行测试。

sh
go test
  • 这将会自动查找当前目录下的所有 _test.go 文件并执行其中的测试函数。

1.3. 测试输出

Go 的测试框架提供了不同的报告方式。默认情况下,测试失败时,go test 会输出类似下面的信息:

--- FAIL: TestAdd (0.00s)
    calculator_test.go:10: Expected 3, but got 4
FAIL
exit status 1
FAIL    calculator 0.004s

2. 基准测试

基准测试用于测试代码的性能。基准测试函数以 Benchmark 开头,并接受一个 *testing.B 类型的参数。

2.1. 编写基准测试

go
// benchmark_test.go
package calculator

import "testing"

// 基准测试 Add 函数
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(1, 2)
    }
}
  • 基准测试的循环次数由 b.N 决定。Go 的基准测试框架会自动调整 b.N,以便获得准确的基准结果。
  • b.N 是测试运行次数,Go 会根据运行时间自动调整。

2.2. 运行基准测试

使用 go test 命令并加上 -bench 标志来运行基准测试:

sh
go test -bench .
  • 这将会执行所有基准测试,并输出性能结果,例如:
BenchmarkAdd-8    2000000    1200 ns/op
  • ns/op 是每次操作的平均纳秒数。

3. 示例测试

Go 也支持示例测试(Example Testing)。示例测试通常用于验证代码示例是否正确。它们的测试函数以 Example 开头。

3.1. 编写示例测试

go
// calculator_test.go
package calculator

import "fmt"

// ExampleAdd 展示如何使用 Add 函数
func ExampleAdd() {
    fmt.Println(Add(1, 2))
    // 输出: 3
}
  • ExampleAdd 函数展示了 Add 函数的使用方法,且在测试过程中会检查其输出是否符合预期。

3.2. 运行示例测试

使用 go test 命令来运行示例测试:

sh
go test -v
  • -v 标志会使测试输出更详细,包括示例测试的输出。

输出会类似于:

=== RUN   ExampleAdd
--- PASS: ExampleAdd (0.00s)
    calculator_test.go:10: 3
PASS
ok      calculator 0.003s

4. 并发测试

Go 的测试框架支持并发执行多个测试。在 Go 的测试中,你可以通过 t.Parallel() 来并行执行测试。

4.1. 并发执行测试

go
func TestAdd(t *testing.T) {
    t.Parallel() // 并发执行此测试

    result := Add(1, 2)
    expected := 3

    if result != expected {
        t.Errorf("Expected %d, but got %d", expected, result)
    }
}
  • t.Parallel() 会告诉 Go 在执行该测试时使用并发执行。这对于测试多个独立操作是非常有用的。

5. 测试覆盖率

Go 提供了覆盖率报告,可以查看代码的哪些部分被测试覆盖到了,哪些没有。你可以使用 -cover 标志来生成覆盖率报告。

5.1. 运行覆盖率报告

sh
go test -cover
  • 输出将包括每个测试函数的覆盖率,例如:
PASS
coverage: 100.0% of statements
ok      calculator 0.004s

5.2. 生成 HTML 格式的覆盖率报告

如果你想查看更详细的覆盖率信息,可以生成 HTML 格式的报告:

sh
go test -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
  • coverage.out 文件包含了测试覆盖率的数据。
  • go tool cover -html=coverage.out -o coverage.html 会生成一个 HTML 文件,你可以在浏览器中查看详细的覆盖率报告。

6. 测试配置和生命周期

6.1. testing.T 的常用方法

  • t.Log():记录测试日志,会在测试成功时显示。

    go
    t.Log("This is a log message")
  • t.Error():记录错误,但继续执行测试。

    go
    t.Error("This is an error")
  • t.Fatal():记录错误并立即停止当前测试。

    go
    t.Fatal("This is a fatal error")
  • t.FailNow():立即停止当前测试,但不报告错误。

6.2. setupteardown

如果你需要在每个测试之前和之后执行一些代码,可以使用 setupteardown 模式,通常通过 t.Cleanup()defer 来实现。

go
func TestSomething(t *testing.T) {
    t.Cleanup(func() {
        // 清理代码,测试结束后调用
        fmt.Println("Cleanup!")
    })

    // 测试逻辑
    fmt.Println("Test logic here!")
}

7. Mock 和断言

Go 标准库的 testing 包没有内建的 mocking 框架和断言库,但你可以使用第三方库,如 github.com/stretchr/testify,它提供了强大的断言功能和 mock 对象。

7.1. 使用 github.com/stretchr/testify

安装 testify 包:

sh
go get github.com/stretchr/testify

使用 testify 的断言功能:

go
package calculator

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestAdd(t *testing.T) {
    result := Add(1, 2)
    assert.Equal(t, 3, result, "Expected result should be 3")
}

8. 总结

  • 单元测试:使用 testing 包编写并运行,测试函数需要以 Test 开头,接受 *testing.T 参数。
  • 基准测试:通过 Benchmark 开头的函数来测试性能,使用 go test -bench 运行。
  • 示例测试:通过 Example 开头的函数来展示如何使用某些函数。
  • 并发测试:可以使用 t.Parallel() 来并发执行测试。
  • 测试覆盖率:可以通过 -cover 标志来查看测试覆盖率,生成详细的报告。
  • 清理操作:使用 t.Cleanup()defer 来处理测试的清理工作。
  • 断言和 Mock:可以使用第三方库(如 testify)来进行更复杂的断言和 mocking。

testing 包为 Go 提供了全面的测试支持,使得编写和执行单元测试变得简洁高效,适用于单元测试、性能测试、示例测试等各种场景。