Go单元测试

server/2024/9/23 9:22:19/

Go 语言中,单元测试是通过标准库中的 testing 包来实现的,该包提供了一组功能,使得编写、运行和管理单元测试变得简单和高效。

一、规则

  • 测试文件的命名规则
    Go 中的测试文件命名规则是在被测试的源文件名后面加上 _test.go。例如,如果你有一个 calculator.go 文件,相应的测试文件应该是 calculator_test.go

  • 测试函数的命名规则
    测试函数必须以 Test 开头,后面可以跟任何非空字符串,例如 TestAddTestSubtract 等。

  • 使用 testing.T 进行断言和错误报告
    在测试函数中,使用 testing.T 类型的参数来管理测试状态和输出。你可以使用 t.Error*t.Fail* 等方法来指示测试失败,并输出相关的错误信息。

二、单元测试示例

2.1 单个测试用例

在calculator包中定义了一个calculator函数,具体实现如下:

package calculatorfunc Add(a, b int) int {return a + b
}

在当前目录下,我们创建一个calculator_test.go的测试文件,并定义一个测试函数如下:

package calculatorimport "testing"func TestAdd(t *testing.T) {result := Add(1, 2)expected := 3if result != expected {t.Errorf("Add(1,2) return %d, expected %d", result, expected)}
}

要运行这个单元测试,可以使用 go test 命令。在命令行中进入到包含 calculator.go 和 calculator_test.go 的目录,然后执行go test,这些结果如下

go test
PASS
ok      modu    0.226s

2.2 多个测试用例

在calculator_test.go中添加如下测试函数:

func TestAdd2(t *testing.T) {result := Add(3, 2)expected := 3if result != expected {t.Errorf("Add(1,2) return %d, expected %d", result, expected)}
}

为了能更好的在输出结果中看到每个测试用例的执行情况,我们可以为go test -v参数,让它输出完整的测试结果。

go test -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestAdd2calculator_test.go:17: Add(1,2) return 5, expected 3
--- FAIL: TestAdd2 (0.00s)
FAIL
exit status 1
FAIL    modu    0.216s

2.3 指定运行测试用例

go test -run 命令可以按照指定的模式运行测试。这个命令支持通过正则表达式来选择要运行的测试函数。

例如修正好TestAdd2用例之后,通过go tes -run=Add2只运行TestAdd2这个测试用例,结果是

go test -run=Add2 -v
=== RUN   TestAdd2
--- PASS: TestAdd2 (0.00s)
PASS
ok      modu    0.198s

4. 跳过某些测试用例
新加测试函数

func TestAdd3(t *testing.T) {if testing.Short() {t.Skip("short模式下会跳过该测试用例")}result := Add(3, 2)expected := 5if result != expected {t.Errorf("Add(1,2) return %d, expected %d", result, expected)}
}

当执行go test -short时,就会跳过testing.Short()标记的测试用例,结果是

go test -short -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestAdd2
--- PASS: TestAdd2 (0.00s)
=== RUN   TestAdd3calculator_test.go:23: short模式下会跳过该测试用例
--- SKIP: TestAdd3 (0.00s)
PASS
ok      modu    0.635s

三、测试组和子测试

3.1 测试组和子测试

通过测试组和子测试,可以更友好来添加更多的测试用例,以及查看结果

func TestAdd(t *testing.T) {tests := []struct {name     stringx, y     intexpected int}{{"Add1", 1, 2, 3},{"Add2", 3, 3, 6},{"Add3", 4, 5, 8},}for _, tc := range tests {t.Run(tc.name, func(t *testing.T) {result := Add(tc.x, tc.y)if result != tc.expected {t.Errorf("Add(%d, %d) returned %d, expected %d", tc.x, tc.y, result, tc.expected)}})}
}

运行go test -v,结果是

go test -v
=== RUN   TestAdd
=== RUN   TestAdd/Add2
=== RUN   TestAdd/Add3calculator_test.go:51: Add(4, 5) returned 9, expected 8
--- FAIL: TestAdd (0.00s)--- PASS: TestAdd/Add1 (0.00s)--- PASS: TestAdd/Add2 (0.00s)--- FAIL: TestAdd/Add3 (0.00s)
FAIL
exit status 1
FAIL    modu    0.190s

3.2 并行测试

Go语言天生支持并发,所以通过添加t.Parallel()来实现驱动测试并行化。

func TestAdd(t *testing.T) {t.Parallel()  // 将 TLog 标记为能够与其他测试并行运行// 这里使用匿名结构体定义了若干个测试用例// 并且为每个测试用例设置了一个名称tests := []struct {name     stringx, y     intexpected int}{{"Add1", 1, 2, 3},{"Add2", 3, 3, 6},{"Add3", 4, 5, 8},}for _, tc := range tests {tc := tc  // 注意这里重新声明tt变量(避免多个goroutine中使用了相同的变量)t.Run(tc.name, func(t *testing.T) {t.Parallel()  // 将每个测试用例标记为能够彼此并行运行result := Add(tc.x, tc.y)if result != tc.expected {t.Errorf("Add(%d, %d) returned %d, expected %d", tc.x, tc.y, result, tc.expected)}})}
}

3.3 测试覆盖率

使用go test -cover来查看测试覆盖率

go test -cover
PASSmodu    coverage: 100.0% of statements
ok      modu    1.149s

四、Go单元测试工具包 – testify

在进行Go语言单元测试时,由于官方并未内置断言功能,我们通常需要使用大量的if...else...语句来校验测试结果。然而,通过使用第三方库如testify/assert,我们可以轻松地调用多种常用的断言函数,这些函数不仅能够简化测试代码,还能生成清晰易懂的错误描述信息,帮助我们快速定位问题。

在上面例子当中,我们使用if...else...语句来校验测试结果

	for _, tc := range tests {t.Run(tc.name, func(t *testing.T) {result := Add(tc.x, tc.y)if result != tc.expected {t.Errorf("Add(%d, %d) returned %d, expected %d", tc.x, tc.y, result, tc.expected)}})}

现在可使用testify/assert之将上述判断过程简化如下:

	for _, tc := range tests {t.Run(tc.name, func(t *testing.T) {result := Add(tc.x, tc.y)assert.Equal(t, result, tc.expected)})}

testify/require拥有testify/assert所有断言函数,它们的唯一区别就是testify/require遇到失败的用例会立即终止本次测试。


http://www.ppmy.cn/server/55460.html

相关文章

六、资产安全—信息分级资产管理与隐私保护(CISSP)

目录 1.信息分级 2.信息分级方法 3.责任的层级 4.资产管理 5.隐私数据管理角色 6.数据安全控制 7.数据保护方案 8.使用安全基线 六、资产安全—数据管理(CISSP): 五、身份与访问管理—身份管理和访问控制管理(CISSP): 1.信息分级 信息分级举列: 2.信息分级方…

go语言DAY7 字典Map 指针 结构体 函数

Go中Map底层原理剖析_go map底层实现-CSDN博客 目录 Map 键值对key,value 注意: map唯一确定的key值通过哈希运算得出哈希值 一、 map的声明及初始化: 二、 map的增删改查操作: 三、 map的赋值操作与切片对比: 四、 通用所有…

node.js环境安装和VUE-cli脚手架搭建

简介 node.js Node.js 是一个免费、开源、跨平台的 JavaScript 运行时环境,它让开发人员能够创建服务器、Web 应用、命令行工具和脚本。 VUE-cli Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供: 通过 vue/cli 实现的交互式的项…

网络安全设备——探针

网络安全设备探针是一种专门用于网络安全领域的工具,它通过对网络流量进行监控和分析,帮助发现和防止网络攻击。以下是对网络安全设备探针的详细解释: 定义与功能 定义:网络安全设备探针是一种设备或软件,它通过捕获…

(七)glDrawArry绘制

几何数据&#xff1a;vao和vbo 材质程序&#xff1a;vs和fs(顶点着色器和片元着色器) 接下来只需要告诉GPU&#xff0c;使用几何数据和材质程序来进行绘制。 #include <glad/glad.h>//glad必须在glfw头文件之前包含 #include <GLFW/glfw3.h> #include <iostrea…

Python面试题:如何在 Python 中反转一个字符串?

在 Python 中反转一个字符串有多种方法&#xff0c;下面详细介绍几种常用的方法&#xff0c;并扩展相关的知识点。 方法1&#xff1a;使用切片 Python 的切片&#xff08;slice&#xff09;功能强大&#xff0c;反转字符串可以通过负步长实现。 # 示例 original_string &qu…

笔记:mysql双主,keepalived 配置

准备工作 1&#xff0c;两台主机,3个IP mysql-1:192.168.0.1 mysql-2:192.168.0.2 vip 192.168.0.3 2&#xff0c;mysql安装包 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 3&#xff0c;keepalived安装包 下载地址&#xff1a;https://www.keepalived.org…

配置jupyter时出现问题?怎么办?

在自己创建的虚拟环境&#xff08;nmjpytorch&#xff09;安装完jupyter&#xff0c;没有跳转到链接&#xff0c;问题如图&#xff1a; 解决方法&#xff1a; 1、查看自己的tornado版本为5.1.1&#xff0c;坑太高了&#xff0c;降低版本为4.5.3 2、卸载tornado-5.1.1 3、安装t…