Golang 单元测试

前言

单元测试是通过编写测试函数来完成的,这些函数位于_test.go文件中

步骤

要创建一个单元测试,你需要遵循以下步骤:

  1. 在与要测试的代码相同的包中创建一个新的文件,文件名以_test.go结尾
  2. 导入testing
  3. 编写测试函数,函数名以Test开头,接受一个*testing.T类型的参数
  4. 在测试函数内部,使用testing.T的方法如Error, Fail, 或 Errorf 来报告失败
  5. 要运行测试,则需打开终端,切换到包含_test.go文件的目录,并运行go test命令,Go 测试工具会自动查找当前目录(或指定包路径)中所有以 _test.go 结尾的文件,执行其中的测试函数

在运行测试命令时,如果目录包括非ASCII字符(如中文字符),那么需要显式指定源文件和测试文件
类似测试指定路径,如:

go test my/package/path

多个则用空格隔开

子测试(t.Run)

子测试(subtests)是一种将相关测试组织在单个测试函数中的方式。这在你想用不同参数或配置运行一组测试时非常有用。下面是一个使用子测试的例子:

func TestAdd(t *testing.T) {
    // 定义一系列测试用例
    testCases := []struct {
        name     string
        a, b     int // 输入参数
        expected int // 期望结果
    }{
        {"两个正数相加", 1, 2, 3},
        {"两个负数相加", -1, -2, -3},
        {"正负数相加", -1, 1, 0},
    }

    // 遍历测试用例
    for _, tc := range testCases {
        // 使用t.Run()定义子测试
        t.Run(tc.name, func(t *testing.T) {
            result := Add(tc.a, tc.b) // 调用待测试的函数
            // 断言:结果是否与期望值相符
            if result != tc.expected {
                t.Errorf("Add(%d, %d) 结果为 %d; 期望值为 %d", tc.a, tc.b, result, tc.expected)
            }
        })
    }
}

在此示例中,t.Run() 用于为每个测试用例定义一个子测试。每个子测试都有一个名称和执行测试的函数。这允许你在测试输出中看到哪些子测试通过或失败。使用 go test 命令结合 -run 标志,你还可以单独运行子测试。

如果你的测试函数中有多个子测试,你可以使用 -run 标志来指定要运行的特定测试或子测试。例如,如果你只想运行名为 “两个正数相加” 的子测试,可以这样做:

go test -run TestAdd/两个正数相加

TestMain函数

如果你需要在测试前设置一些全局状态或者在所有测试运行完后执行清理工作,可以使用TestMain函数。这个函数应该在你的测试文件中定义,并且只能定义一次。它允许你控制测试的运行方式,包括手动调用测试函数。

TestMain函数通常接受一个*testing.M参数,并且没有返回值。它可以调用m.Run()来启动测试,该方法将返回一个状态码,通常会将这个状态码作为参数传递给os.Exit来结束程序。

以下是TestMain的使用示例:

package mypackage

import (
    "os"
    "testing"
)

func setup() {
    // 在测试运行前执行的代码
}

func teardown() {
    // 在所有测试运行完后执行的代码
}

func TestMain(m *testing.M) {
    setup()
    // 执行测试
    code := m.Run()
    teardown()
    // 退出程序
    os.Exit(code)
}

setup函数会在任何测试运行之前执行,而teardown函数则在所有测试完成后执行。m.Run()在setup和teardown之间调用,它实际上运行所有的测试函数。然后,你可以使用它的返回值作为参数传递给os.Exit,以正确地设置程序的退出状态码。

如果你的测试文件中定义了TestMain,Go的测试运行器将调用这个函数而不是直接运行测试函数。这给了你更多的控制权,但同时也意味着你必须显式调用m.Run()来确保测试的执行

go test 常见用法

  • 运行特定的测试函数:
go test -run TestName

其中 TestName 是要运行的测试函数的名称。支持正则表达式,例如 -run 'TestAdd$' 只会运行 TestAdd 测试函数

  • 显示测试覆盖率:
go test -cover

这会输出测试的覆盖率统计信息

  • 生成测试覆盖率文件
go test -coverprofile=coverage.out

创建一个名为 coverage.out 的文件,其中包含覆盖率数据

  • 查看覆盖率报告
go tool cover -html=coverage.out

会打开一个网页,显示详细的覆盖率报告

  • 运行带有详细输出的测试
go test -v

-v 标志将会输出每个测试函数的名称和运行结果

  • 运行特定的测试包
go test ./...

使用 ./… 运行当前目录及其所有子目录中的测试

  • 运行基准测试
go test -bench .

-bench 参数可以接受一个正则表达式,用来指定要运行的基准测试函数名称

  • 设置测试时的环境变量
go test -env "FOO=bar"

这将在测试运行时设置环境变量 FOO 的值为 bar

  • 并行运行测试
go test -parallel n
  • 这些选项可以结合使用,以适应你的具体测试需求
    例如,你可以同时运行所有测试,显示详细输出并获取覆盖率报告:
go test -v -coverprofile=coverage.out ./...

你可能感兴趣的:(golang,单元测试)