20 | 如何添加单元测试用例

提示:

  • 所有体系课见专栏:Go 项目开发极速入门实战课;
  • 欢迎加入 云原生 AI 实战 星球,12+ 高质量体系课、20+ 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);
  • 本节课最终源码位于 fastgo 项目的 feature/s16 分支;
  • 更详细的课程版本见:Go 项目开发中级实战课:33 | 项目测试:如何开发单元测试用例?

在实际开发中,不仅要开发功能,而且还要确保这些功能稳定可靠,并且拥有一个不错的性能,要确保这些,就要对代码进行测试。测试分为很多种,例如:功能测试、性能测试、集成测试、端到端测试、单元测试等。

对于开发者来说,需要执行的测试种类一般是单元测试和性能测试。除此之外,Go 还提供了其他类型的测试,例如:模糊测试、示例测试。本节课会详细介绍开发者需要重点关注的单元测试和性能测试用例。

Go 标准库 testing 包介绍

Go 语言自带测试框架 testing,可以用于编写单元测试用例和性能测试用例,并通过 go test 命令运行测试用例。

go test 命令在运行测试用例时,以 Go 包为单位进行测试。运行时需要指定包名,例如:go test <包名>。如果未指定包名,测试将默认作用于运行命令时所在的包。go test 执行时会遍历以 _test.go 结尾的源码文件,并运行其中以 TestBenchmarkExampleFuzz 开头的测试函数。这些源码文件需满足以下规则:

  • 文件名要求: 文件名必须以 _test.go 结尾,且建议与被测试的源文件位于同一个包中;
  • 测试用例函数规范: 测试用例函数需以 TestBenchmarkExampleFuzz 开头;
  • 测试执行顺序: 测试用例的执行顺序按照源码中的定义顺序依次进行;
  • 单元测试函数: 函数名称形如 TestXxx(t *testing.T),其中 Xxx 部分为任意字母数字组合,首字母需大写。例如:Testlogger 是错误的函数名,TestLogger 是正确的函数名。参数 testing.T 可以用于记录错误或测试状态;
  • 性能测试函数: 函数名称形如 BenchmarkXxx (b *testing.B),函数以 b.N 作为循环次数,其中 N 值会动态变化;
  • 示例函数: 示例函数名称形如 ExampleXxx(),没有参数,执行后将其输出与注释 // Output: 中声明的结果进行对比。

testing.T 提供了丰富的方法来管理测试过程和结果,常用方法如下:

  • 输出测试信息:t.Logt.Logf 两个方法可以用来输出测试信息;
  • 输出测试失败信息:t.Errort.Errorf 两个方法可以用来输出测试异常或失败时的信息;
  • 记录致命错误:t.Fatalt.Fatalf 两个方法用来记录致命错误,并退出测试;
  • 标记测试失败:t.Fail 方法用来将当前测试标记为失败,但测试不会退出。t.Failed 方法用来检查当前测试是否已标记为失败;
  • 终止测试:t.FailNow 用于标记当前测试失败,并立即终止当前测试函数的执行;
  • 跳过测试:t.Skipt.Skipf 两个方法可用于跳过当前测试函数的执行,并记录一条备注信息。t.Skipped 方法可用于检测当前测试是否已被跳过;
  • 并行执行测试:t.Parallel 可将测试标记为支持并行运行。

性能测试过程中,需要重点注意 BenchmarkXxx 函数,其参数 testing.B 用于设置动态变化的循环次数(b.N 值),例如:

func BenchmarkResourceID_New(b *testing.B) {// 性能测试b.ResetTimer()for i := 0; i < b.N; i++ {userID := rid.UserID_ = userID.New(uint64(i))}
}

Go 测试用例编写

在实际项目开发中,最常编写的是单元测试用例,其次是性能测试用例。在某些场景下,还可能需要编写模糊测试用例和示例测试用例。本节会详细介绍如何编写单元测试、性能测试。

单元测试

新建 internal/pkg/rid/rid_test.go 文件,在文件中添加 ResourceID 数据类型 String() 方法的单元测试用例:

func TestResourceID_String(t *testing.T) {// 测试 UserID 转换为字符串userID := rid.UserIDassert.Equal(t, "user", userID.String(), "UserID.String() should return 'user'")// 测试 PostID 转换为字符串postID := rid.PostIDassert.Equal(t, "post", postID.String(), "PostID.String() should return 'post'")
}

在编写单元测试用例时,经常需要对比期望值和实际值是否一致,可以直接编写代码来对比,例如:

if expected != actual {t.Error("actual value did not match the expected value")
}

但更建议使用优秀的断言包来对比。在 Go 生态中,比较常用的断言包是 github.com/stretchr/testify/assert。

assert 包,提供了一组丰富的断言函数,用于简化 Go 语言中的单元测试用例编写。通过 assert 包,开发者可以用更直观的方式验证测试结果,如检查值相等、布尔值匹配、集合包含关系、错误状态等,从而极大地提测试代码的可读性和开发效率。

实现单元测试用例后,可以通过执行 go test 命令运行测试用例。go test 命令支持不同的命令行选项,从而实现多种测试效果。常用的 go test 命令如下。

(1)执行默认的测试用例

在 internal/pkg/rid 目录下执行命令 go test

$ go test .
ok  github.com/onexstack/miniblog/internal/pkg/rid0.008s

(2)查看更详细的执行信息

要查看更详细的执行信息可以执行 go test -v

 $ go test -v .
=== RUN   TestResourceID_String
--- PASS: TestResourceID_String (0.00s)
=== RUN   TestResourceID_New
--- PASS: TestResourceID_New (0.00s)
=== RUN   FuzzResourceID_New
=== RUN   FuzzResourceID_New/seed#0
=== RUN   FuzzResourceID_New/seed#1
--- PASS: FuzzResourceID_New (0.00s)--- PASS: FuzzResourceID_New/seed#0 (0.00s)--- PASS: FuzzResourceID_New/seed#1 (0.00s)
PASS
ok  	github.com/onexstack/fastgo/internal/pkg/rid	0.007s

(3)执行测试 N 次

如果要执行测试 N 次可以使用 -count N 命令行选项:

$ go test -v -count 2
=== RUN   TestResourceID_String
--- PASS: TestResourceID_String (0.00s)
=== RUN   TestResourceID_New
--- PASS: TestResourceID_New (0.00s)
=== RUN   TestResourceID_String
--- PASS: TestResourceID_String (0.00s)
=== RUN   TestResourceID_New
--- PASS: TestResourceID_New (0.00s)
=== RUN   FuzzResourceID_New
=== RUN   FuzzResourceID_New/seed#0
=== RUN   FuzzResourceID_New/seed#1
--- PASS: FuzzResourceID_New (0.00s)--- PASS: FuzzResourceID_New/seed#0 (0.00s)--- PASS: FuzzResourceID_New/seed#1 (0.00s)
=== RUN   FuzzResourceID_New
=== RUN   FuzzResourceID_New/seed#0
=== RUN   FuzzResourceID_New/seed#1
--- PASS: FuzzResourceID_New (0.00s)--- PASS: FuzzResourceID_New/seed#0 (0.00s)--- PASS: FuzzResourceID_New/seed#1 (0.00s)
PASS
ok  	github.com/onexstack/fastgo/internal/pkg/rid	0.007s

通过上述测试输出可知,每个测试用例被执行了 2 次。

(4)只运行指定的单测用例

此外,你还可以通过指定 -run 参数(-run 参数支持正则表达式)只运行指定的单测用例:

$ go test -v -run TestResourceID_String
=== RUN   TestResourceID_String
--- PASS: TestResourceID_String (0.00s)
PASS
ok  	github.com/onexstack/fastgo/internal/pkg/rid	0.007s

性能测试

性能测试也叫基准测试,是 Go 项目开发中,非常核心的测试用例类型。Go 开发者也需要掌握如何编写性能测试用例。

在 internal/pkg/rid/rid_test.go 文件中,新增 BenchmarkResourceID_New 性能测试用例函数,代码如下:

func BenchmarkResourceID_New(b *testing.B) {// 性能测试b.ResetTimer() for i := 0; i < b.N; i++ {userID := rid.UserID_ = userID.New(uint64(i))}
} 

上述代码定义了一个基准测试函数,用于测量 userID.New 方法的性能表现。函数通过 b.ResetTimer() 重置计时器,确保计时只统计核心测试代码的执行时间,然后在一个循环中根据 b.N 的值多次调用 userID.New(uint64(i)) 方法,以模拟高频调用场景并评估其性能。

性能测试函数的名称必须以 Benchmark 开头,例如 BenchmarkXxxBenchmark_Xxx。默认情况下,go test 不会执行性能测试函数,需通过指定参数 -test.bench 来运行,-test.bench 后需接正则表达式,例如 go test -test.bench=".*" 表示运行所有性能测试函数。在性能测试中,应在循环体中使用 testing.B.N 来多次循环执行测试代码。

在编写性能测试用例时,如果用例需要进行一些耗时的准备工作以测试目标函数,可以在准备工作完成后调用 b.ResetTimer() 方法重置计时器。

实现性能测试用例后,可以执行 go test 命令来运行性能测试用例。在 internal/pkg/rid 目录下,执行 go test -test.bench=".*" 命令来运行性能测试用例:

$ go test -test.bench=".*"
goos: linux
goarch: amd64
pkg: github.com/onexstack/fastgo/internal/pkg/rid
cpu: Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz
BenchmarkResourceID_New-32    	  180157	      6558 ns/op
PASS
ok  	github.com/onexstack/fastgo/internal/pkg/rid	1.262s

上述测试用例执行结果显示,BenchmarkResourceID_New 用例执行了 180157 次,每次执行的平均时间是 6558 纳秒。1.262s 表示测试用例总的执行时间。

在运行性能测试用例时,还可以通过 -benchtime 命令行选项,来指定性能测试用例的运行时间和运行次数,确保性能测试结果更加稳定,Go 会根据指定的运行时间和运行次数动态调整运行次数(b.N),以确保测试运行的总时长接近设定值。二者的指定方式如下:

  • -benchtime=1x:指定运行次数为 1 次(可改为任意次数,例如 -benchtime=10x 表示运行 10 次);
  • -benchtime=5s:指定基准测试运行时间为 5 秒(可改为其他时间,例如 -benchtime=100ms 表示运行 100 毫秒)。

运行以下命令,并分别指定性能测试用例的运行时间为 30s、运行次数为 100000 次:

$ go test -benchtime=30s -test.bench="^BenchmarkResourceID_New$"
goos: linux
goarch: amd64
pkg: github.com/onexstack/fastgo/internal/pkg/rid
cpu: Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz
BenchmarkResourceID_New-32    	 5459558	      6700 ns/op
PASS
ok  	github.com/onexstack/fastgo/internal/pkg/rid	43.255s
$ go test -benchtime=100000x -test.bench="^BenchmarkResourceID_New$"
goos: linux
goarch: amd64
pkg: github.com/onexstack/fastgo/internal/pkg/rid
cpu: Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz
BenchmarkResourceID_New-32    	  100000	      6507 ns/op
PASS
ok  	github.com/onexstack/fastgo/internal/pkg/rid	0.661s

在实际运行性能测试用例时,通常会指定运行时间而非运行次数。

测试覆盖率分析

在编写单元测试时,应尽量考虑全面,覆盖所有可能的测试用例,但有时仍可能遗漏一些测试用例。Go 提供了 cover 工具用于统计测试覆盖率。测试覆盖率可以通过以下两条命令完成:

  • go test -coverprofile=cover.out:在测试文件目录下运行测试并统计测试覆盖率;
  • go tool cover -func=cover.out:分析覆盖率文件,用于检查哪些函数未被测试,或者哪些函数内部的分支未完全覆盖。cover 工具通过执行代码的行数与总行数的比例来表示覆盖率。

进入 internal/pkg/rid 目录,执行以下命令,来测试单元测试覆盖率:

$ cd internal/pkg/rid
$ go test -coverprofile=cover.out
$ go tool cover -func=cover.out
github.com/onexstack/fastgo/internal/pkg/rid/rid.go:25:		String			100.0%
github.com/onexstack/fastgo/internal/pkg/rid/rid.go:30:		New			100.0%
github.com/onexstack/fastgo/internal/pkg/rid/salt.go:18:	Salt			100.0%
github.com/onexstack/fastgo/internal/pkg/rid/salt.go:29:	ReadMachineID		72.7%
github.com/onexstack/fastgo/internal/pkg/rid/salt.go:50:	readPlatformMachineID	75.0%
total:								(statements)		81.8%

可以看到 github.com/onexstack/fastgo/internal/pkg/rid 包的单元测试覆盖率为 81.8%

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/32398.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安全左移动赋能:灵脉IAST交互式应用安全测试平台

左移的安全赋能 Earlier Security Empowerment 悬镜灵脉IAST灰盒安全测试平台作为国内领先的交互式应用安全测试平台&#xff0c;透明集成于现有IT流程&#xff0c;自动化完成业务代码上线前安全测试&#xff0c;重点覆盖90%以上中高危漏洞&#xff0c;防止应用带病上线&…

Linux下学【MySQL】常用函数助你成为数据库大师~(配sql+实操图+案例巩固 通俗易懂版~)

绪论​ 每日激励&#xff1a;“唯有努力&#xff0c;才能进步” 绪论​&#xff1a; 本章是MySQL中常见的函数&#xff0c;利用好函数能很大的帮助我们提高MySQL使用效率&#xff0c;也能很好处理一些情况&#xff0c;如字符串的拼接&#xff0c;字符串的获取&#xff0c;进制…

波特率、比特率、传信率、传码率......

去年搞过一段时间的无线通信&#xff0c;当时我脑子里真的是一团乱麻&#xff0c;本身咱也不是学通信的&#xff0c;咱是学机械出身的&#xff0c;后面又搞电&#xff0c;反正对于通信这一块就不是很懂&#xff0c;后面也慢慢搞出来了一点小东西&#xff0c;但是对于一些细节还…

华为HCIE认证用处大吗?

新盟教育 专注华为认证培训十余年 为你提供认证一线资讯&#xff01; 在ICT行业的认证体系中&#xff0c;华为HCIE认证一直备受关注。那么&#xff0c;华为HCIE认证用处大吗&#xff1f;今天咱们就来深入探讨一下&#xff0c;以数据通信方向为例&#xff0c;看看它到底能带来什…

【WRF-Chem】预处理工具(Preprocessors)总结

WRF-Chem 预处理工具&#xff08;Preprocessors&#xff09; 化学选项&#xff08;Chemistry Options&#xff09;数据下载 预处理工具&#xff08;Preprocessors&#xff09;工具1&#xff1a;mozbc工具2&#xff1a;bio_emiss工具3&#xff1a;anthro_emiss工具4&#xff1a;…

六、OpenGL中EBO的使用及本质

文章目录 一、什么是顶点索引二、什么是EBO三、EBO使用的完整代码 一、什么是顶点索引 OpenGL 中&#xff0c;顶点索引&#xff08;Vertex Index&#xff09;用于减少重复的顶点数据&#xff0c;提高绘制效率。其核心概念涉及索引缓冲对象&#xff08;Index Buffer Object&…

Python+jupyter进行数据分析与数据挖掘

随着人工智能的发展&#xff0c;现在越来越多人使用Python语言进行数据分析。Python在数据分析中有哪些优势呢&#xff1f;由于Python中有很多的第三方插件&#xff0c;接下来我们探讨Pythonjupyter的结合&#xff0c;在数据分析领域中的应用。 一、jupyter介绍 Jupyter 是一个…

AI4CODE】3 Trae 锤一个贪吃蛇的小游戏

【AI4CODE】目录 【AI4CODE】1 Trae CN 锥安装配置与迁移 【AI4CODE】2 Trae 锤一个 To-Do-List 这次还是采用 HTML/CSS/JAVASCRIPT 技术栈 Trae 锤一个贪吃蛇的小游戏。 1 环境准备 创建一个 Snake 的子文件夹&#xff0c;清除以前的会话记录。 2 开始构建 2.1 输入会…

PostgreSQL17(最新版)安装部署

PostgreSQL 17已与2024年9月26日正式发布&#xff01;&#xff01;&#xff01; 一、Postgres概述 官网地址&#xff1a;PostgreSQL: The world’s most advanced open source database Postgres作为最先进的开源数据库&#xff08; the latest version of the world’s most…

捌拾贰- 贝尔不等式 (2)

1. 贝尔不等式理解 我感觉我前期理解的不是很对 柒拾玖- 贝尔不等式 … 思来想去几天&#xff0c;感觉贝尔不等式应该是这样来的 因为观测的值只有可能是 1 (别问我为什么) , 设观测角度 Q 值为 1 的概率为 a , -1 的概率为 b , Q 的数学期望值为 E(Q) a * 1 b * (-1) a…

小凯的疑惑(数论 )

#include <iostream> using namespace std; typedef long long ll; int main() {// 请在此输入您的代码ll a,b;cin>>a>>b;ll N a * b - a - b;cout << N ;return 0; } 如果 a 和 b 互素&#xff0c;那么 a * b - a - b 是最大无法被表示的金额

Android内存泄漏检测与优化

Android内存泄漏检测与优化 一、内存泄漏基础知识 1.1 什么是内存泄漏 在Android开发中&#xff0c;内存泄漏(Memory Leak)是指程序在申请内存后&#xff0c;无法释放已申请的内存空间&#xff0c;导致系统可用内存减少的问题。随着泄漏内存的增加&#xff0c;应用可能会变得…

51单片机Proteus仿真速成教程——P1-软件与配置+Proteus绘制51单片机最小系统+新建程序模版

前言&#xff1a;本文主要围绕 51 单片机最小系统的绘制及程序模板创建展开。首先介绍了使用 Proteus 绘制 51 单片机最小系统的详细步骤&#xff0c;包括软件安装获取途径、工程创建、器件添加&#xff08;如单片机 AT89C51、晶振、电容、电阻、按键等&#xff09;、外围电路&…

微信小程序校园跑腿的设计与实现【lw+源码+部署+视频+讲解】

第一章 绪论 1.1 本课题研究背景 近年来城市与社会经济发展较快&#xff0c;人们的生活水平不断提高&#xff0c;消费观念发生很大变化&#xff0c;随着 微信小程序技术的发展&#xff0c;小程序已经渗透到人们日常生活的方方面面&#xff0c;悄悄地改变着人们的生活方式。在…

多用户网页在线聊天室(测试报告)

文章目录 多用户网页在线聊天室一&#xff0c;项目概括1.1 项目名称1.2 测试时间1.3 项目背景1.3 编写目的 二&#xff0c;测试计划2.1 测试环境与配置2.2 测试用例2.3实际执行用例2.3.1登录2.3.2聊天消息列表展示2.3.3聊天消息详情页展示2.3.4联系人页展示2.3.5信息的编辑与发…

自由学习记录(43)

不同的服务器可以使用不同协议&#xff0c;但协议本身不会决定服务器的类型 类型特点物理服务器真实的计算机&#xff08;如 Dell、HP 服务器&#xff09;虚拟服务器运行在云计算平台上的 VM&#xff08;如 AWS EC2、阿里云 ECS&#xff09;容器化服务器通过 Docker / Kuberne…

Vue框架

一. 什么是Vue 1. Vue是一款用于构建用户界面的渐进式的JavaScript框架。(官方&#xff1a;https://cn.vuejs.org/) 2. 框架&#xff1a;就是一套完整的项目解决方案&#xff0c;用于快速构建项目 3. 优点&#xff1a;大大提升前端项目的开发效率 4. 缺点&#xff1a;需要理解记…

强大的数据库DevOps工具:NineData 社区版

本文作者司马辽太杰&#xff0c; gzh&#xff1a;程序猿读历史 在业务快速变化与数据安全日益重要的今天&#xff0c;生产数据库变更管理、版本控制、数据使用是数据库领域的核心挑战之一。传统的解决方式往往采用邮件或即时通讯工具发起审批流程&#xff0c;再通过堡垒机直连数…

数字IC后端设计实现教程 |Innovus ICC2 Routing Pin Access Setting设置方法

默认情况下routing 引擎可以在标准单元可以打孔的任何地方&#xff08;via region&#xff09;打孔&#xff0c;甚至工具还会先拉出一块metal&#xff0c;然后再打孔过渡到高层。 随之工艺节点越做越小&#xff0c;标准单元内部的结构也越来越复杂。此时如果还沿用传统工艺的走…

珠算之珠心算观想算盘

一个好的观想算盘&#xff0c;会对珠心算学习效率的提高起到巨大的促进作用。 在传统的珠心算教学中&#xff0c;人们在观想算盘时&#xff0c;基本都是以自己手中所拿的实际算盘为参照模型进行观想的。由于市场上的算盘样式繁多&#xff0c;学生观想算盘时的参照算盘也是五花…