1、如何编写单元测试

在任何生产级别的项目开发中,单元测试都扮演着至关重要的角色。尽管许多初创项目在早期可能忽略了它,但随着项目逐渐成熟并成为核心业务,为其编写健壮的单元测试是保障代码质量和项目稳定性的必然选择。本文将带您快速掌握 Go 语言中单元测试的基本方法和核心概念。

1、核心命令:go test

Go 语言的测试工具链非常简洁,其核心是 go test 命令。这是一个依据特定约定来组织和驱动测试代码的程序。当你在一个包目录中执行 go test 时,它会自动寻找并执行所有符合测试规范的用例。

2、测试文件的约定

go test 命令的运行依赖于一套简单的文件和函数命名约定:

  1. 文件名约定:在包目录中,所有以 _test.go 为后缀的源文件都会被 go test 命令识别为测试文件并执行。
  2. 构建时排除:您无需担心测试文件会增加最终可执行文件的大小。go build 命令在编译和打包时会自动忽略这些 _test.go 文件,确保它们只存在于测试环境中。

3、测试函数的类型

_test.go 文件中,我们主要会编写以下几种类型的测试函数,目前我们主要关注前两种:

  • 功能测试 (Functional Tests):函数名以 Test 开头,例如 TestMyFunction。这是最常见的测试类型,用于验证代码功能的正确性。
  • 性能测试 (Benchmark Tests):函数名以 Benchmark 开头,例如 BenchmarkMyFunction。用于衡量代码的性能和效率。
  • 示例测试 (Example Tests):函数名以 Example 开头,用于提供可执行的示例代码,常用于文档生成。
  • 模糊测试 (Fuzzing Tests):以 Fuzz 开头,是一种自动化的测试技术,用于发现边界条件下的潜在错误。

4、编写第一个单元测试

让我们通过一个具体的例子来演示如何编写和运行一个单元测试。

1. 准备被测试的代码

首先,我们创建一个 add.go 文件,并在其中定义一个简单的加法函数。

image.png

2. 创建测试文件

接下来,在与 add.go 相同的包(目录)下,我们创建测试文件 add_test.go。将测试文件和源文件放在同一包内,可以方便地测试包内未导出的(小写字母开头的)函数和方法。

image.png

3. 编写测试函数

add_test.go 文件中,我们编写一个功能测试函数来验证 Add 函数的正确性。

测试函数的规范:

  • 函数名必须以 Test 开头,后面通常跟上被测试的函数名,如 TestAdd
  • 参数必须是 t *testing.T*testing.T 类型提供了报告测试失败和记录日志等核心功能。

image.png

编写完成后,许多 IDE(如 GoLand)会自动在函数旁显示一个可运行的标记,这表明 IDE 已经识别出这是一个有效的测试用例。

2、管理和跳过耗时测试

当测试用例数量增多时,某些测试(如涉及网络请求或大量计算的测试)可能会运行得非常缓慢。为了在开发过程中获得快速反馈,我们常常希望可以跳过这些耗时的测试。

Go 语言为此提供了 -short 模式。

工作原理

  1. 在运行测试时,可以附加 -short 标志:go test -v -short
  2. 在测试函数内部,可以通过 testing.Short() 函数进行判断。如果 -short 标志被设置,该函数返回 true
  3. 使用 t.Skip() 方法来跳过当前测试。当 t.Skip() 被调用时,该测试函数会立即终止,并被标记为“已跳过”(SKIPPED),而不会被标记为“失败”(FAILED)。

让我们添加一个模拟的耗时测试 TestLongRunningTask

.正常模式

执行 go test -v,所有测试都会运行,包括耗时的测试。

image.png

可以看到,总耗时超过了2秒。

b. Short 模式

执行 go test -v -short,耗时的测试将被跳过。

image.png

可以看到,TestLongRunningTask 被标记为 SKIP,并且总耗时非常短。通过这种方式,我们可以灵活地控制运行哪些测试用例,从而提升开发效率。

3、表格驱动测试 (Table-Driven Tests)

当我们需要用多组不同的输入和期望输出来测试同一个函数时(例如,测试正常情况、边界情况、异常情况),为每一组数据编写一个独立的 Test 函数会非常繁琐且难以维护。

表格驱动测试模式优雅地解决了这个问题。其核心思想是将所有测试用例定义在一个“表格”(通常是一个结构体切片)中,然后在一个测试函数内遍历这个表格,执行每一个测试用例。

实现步骤

  1. 定义测试用例结构体:创建一个结构体,用于描述一个完整的测试用例,包含输入参数和期望的输出结果。
  2. 创建测试用例表格:声明一个该结构体的切片,并填充所有需要测试的数据。
  3. 遍历表格执行测试:在测试函数中,使用 for 循环遍历切片。对于每个测试用例,执行被测函数并断言结果。
  4. (推荐) 使用 t.Run 创建子测试:在循环中为每个测试用例创建一个子测试。这样做的好处是,所有测试用例都会被执行(即使中途有失败),并且测试结果会清晰地分组展示,便于定位问题。

代码示例

让我们用表格驱动模式来重构 TestAdd

image.png

当我们运行这个测试时,如果出现错误(例如,我们将第三个用例的期望值 expected 错写成 0),会得到非常清晰的报告。

image.png

这个输出明确地告诉我们,只有名为 input:_-9,_8 的子测试失败了,极大地提高了调试效率。

4、性能测试 (Benchmark Tests)

除了功能正确性,代码的性能也是衡量质量的重要维度。对于一些位于核心路径、对性能有高要求的函数,我们需要进行性能测试。Go 语言内置了强大的性能测试框架。

性能测试的规范

  • 函数命名:性能测试函数必须以 Benchmark 开头,例如 BenchmarkAdd
  • 函数签名:参数必须是 b *testing.B*testing.B 类型提供了控制计时器、设置迭代次数等能力。
  • 核心循环:测试的主体逻辑必须放在一个 for 循环内,循环次数由 b.N 决定:for i := 0; i < b.N; i++go test 命令会自动调整 b.N 的值,反复运行代码直到获得稳定可靠的测量结果。

简单示例

image.png

实践:比较字符串拼接性能

字符串拼接是一个非常常见的操作,不同的实现方式性能差异巨大。下面我们通过性能测试来实际比较三种方法的优劣:fmt.Sprintf+ 操作符和 strings.Builder

image.png

通过命令行运行测试 使用 go test 命令并附加 -bench 标志。. 作为参数表示运行当前包下所有的性能测试。

go test -bench=.

运行获取到的结果,具体数值取决于您的机器性能。

根据提供的基准测试结果,以下是各个字符串拼接方法的性能对比分析:

  1. [BenchmarkStringSprintf](file:///Users/jie/Desktop/code/go/onego/xh02/add_test.go#L12-L20)(使用 fmt.Sprintf 拼接)

    • 执行次数:88 次
    • 每次操作耗时:约 13,797,447 ns (13.8 ms)
    • 分析:性能最差,因为 fmt.Sprintf 在每次调用时都需要进行格式解析和内存分配,效率较低。
  2. [BenchmarkStringAdd](file:///Users/jie/Desktop/code/go/onego/xh02/add_test.go#L23-L31)(使用 + 操作符拼接)

    • 执行次数:99 次
    • 每次操作耗时:约 11,765,825 ns (11.8 ms)
    • 分析:比 fmt.Sprintf 快,但由于字符串是不可变类型,每次 + 操作都会创建新字符串并复制内容,性能依然有限。
  3. [BenchmarkStringBuilder](file:///Users/jie/Desktop/code/go/onego/xh02/add_test.go#L34-L43)(使用 strings.Builder 拼接)

    • 执行次数:9418 次
    • 每次操作耗时:约 123,530 ns (0.12 ms)
    • 分析:性能最优。strings.Builder 是专为高效字符串拼接设计的类型,内部使用 []byte 缓冲区,避免了频繁的内存分配和复制。

总结:

  • strings.Builder 明显优于其他两种方式,尤其在大量字符串拼接操作中表现最佳。
  • 避免在循环中使用 fmt.Sprintf+ 进行字符串拼接,除非对性能要求不高。
  • 推荐在性能敏感场景下优先使用 strings.Builder

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

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

相关文章

8. 接口专业测试报告生成pytest-html

pytest-html 终极指南&#xff1a;打造专业级接口测试报告 在接口自动化测试中&#xff0c;清晰的测试报告是质量保障的关键。本文将深入解析如何通过pytest-html插件生成专业级测试报告。 一、核心安装与基础使用 快速安装&#xff08;国内镜像&#xff09; pip install -i …

Day45 Tensorboard使用介绍

目录 一、tensorboard的发展历史和原理及基本操作 1.1 发展历史 1.2 tensorboard的原理 1.3 日志目录自动管理 1.4 记录标量数据&#xff08;Scalar&#xff09; 1.5 可视化模型结构&#xff08;Graph&#xff09; 1.6 可视化图像&#xff08;Image&#xff09; 1.7 记…

用AI给AR加“智慧”:揭秘增强现实智能互动的优化秘密

用AI给AR加“智慧”:揭秘增强现实智能互动的优化秘密 引子:增强现实,到底还能怎么更聪明? 还记得当年Pokmon GO火爆全球的场景吗?玩家们手机对准街头,虚拟小精灵活灵活现地跳出来,那就是增强现实(AR)最经典的应用之一。随着硬件发展和算法进步,AR正逐步从“炫酷玩具…

1 Studying《Computer Vision: Algorithms and Applications 2nd Edition》1-5

目录 Chapter 1 Introduction 1.1 什么是计算机视觉&#xff1f; 1.2 简史 1.3 书籍概述 1.4 样本教学大纲 1.5 符号说明 1.6 其他阅读材料 Chapter 2 Image formation 2.1 几何基本元素和变换 2.2 光度图像形成 2.3 数码相机 2.4 其他阅读材料 2.5 练习 Chapter…

Augment插件macOS

macOS苹果电脑vscode-augment免费额度续杯跑满 前言 在AI辅助编程日益普及的今天&#xff0c;Augment作为VS Code中的智能代码助手&#xff0c;为开发者提供了强大的代码生成和优化功能。然而&#xff0c;免费版本每月300次的使用限制往往让重度用户感到困扰。本文将详细介绍如…

OpenCV CUDA模块设备层-----创建一个“常量指针访问器” 的工具函数constantPtr()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 CUDA 设备端模拟一个“指向常量值”的虚拟指针访问器&#xff0c;使得你可以像访问数组一样访问一个固定值。 这在某些核函数中非常有用&…

Python:操作 Excel 删除工作簿

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…

Python类型注解(Type Hints)的工程实践指南

一、类型注解的核心价值 代码可读性&#xff1a;明确函数输入输出类型 静态检查&#xff1a;配合mypy提前发现类型错误 IDE支持&#xff1a;提升代码补全和重构能力 文档替代&#xff1a;类型即文档的现代编程理念 二、基础语法规范 def greet(name: str, times: int 1)…

Hadoop RPC 分层设计的哲学:高内聚、低耦合的最佳实践

Hadoop RPC Hadoop RPC主要分为四个部分&#xff0c;分别是序列化层、函数调用层、网络传输层和服务器端处理框架&#xff0c;实现机制为&#xff1a; 序列化层&#xff1a;主要作用是将结构化对象转为字节流以便于通过网络进行传输或写入持久存储。函数调用层&#xff1a;主…

MybatisPlus-01.MybatisPlus介绍

一.MybatisPlus介绍 MybatisPlus是对Mybatis的增强和升级&#xff0c;但需要注意的是&#xff0c;MybatisPlus并不是取代Mybatis的&#xff0c;而是要做Mybatis最好的合作伙伴。左边蓝色的小鸟就是MybatisPlus的标志。 在MybatisPlus官方页面上介绍了其特点&#xff0c;首先&am…

人大金仓数据库jdbc连接jar包kingbase8-8.6.0.jar驱动包最新版下载(不需要积分)

看了网上的很多&#xff0c;都是需要下载积分的 分享一下直接访问人大金仓官网&#xff0c;下载对应的数据库jdbc连接jar包kingbase8-8.6.0.jar驱动包&#xff1a; 点击 服务与支持&#xff0c;然后选择 下载中心 选择对应的产品和版本&#xff0c;最后选择软件版本 看到有…

cf 禁止http/1.0和http/1.1的访问 是否会更安全?

使用 Cloudflare&#xff08;CF&#xff09;禁止 HTTP/1.0 和 HTTP/1.1 的访问&#xff0c;强制客户端使用 HTTP/2 或更高版本&#xff08;如 HTTP/3&#xff09;&#xff0c;在某些情况下可以提升网站安全性&#xff0c;但也存在权衡和限制。以下是详细分析&#xff0c;帮你判…

【Docker基础】Docker容器管理:docker pause详解

目录 1 Docker容器管理概述 2 docker pause命令详解 2.1 命令基本语法 2.2 命令功能解析 2.3 暂停与停止的区别 3 docker pause的工作流程 3.1 工作流程概述 3.2 工作流程详解 4 docker pause的使用场景 4.1 资源临时调整 4.2 调试与检查 4.3 服务维护 4.4 数据备…

Springboot ResponseBodyAdvice 的小妙用

最近公司接触到了政府项目&#xff0c;在开发完成后&#xff0c;需要对代码做安全扫描&#xff0c;对系统做安全测试&#xff0c;在安全测试中有一项不合格&#xff0c;就是接口返回错误是&#xff0c;错误不是浏览器级别的&#xff0c;什么意思呢&#xff0c;一般我们都会封装…

Re:从零开始的文件结构(融合线性表来理解 考研向)

文件管理 & 线性表 文件管理文件的结构无结构文件 有结构文件&#xff08;重点&#xff09;定长与不定长记录顺序文件&#xff08;类线性表&#xff09;它的逻辑结构它的物理结构&#xff08;存储结构&#xff09;小结 索引顺序文件与多级索引顺序文件形象化理解&#xff0…

并发基础7(守护线程)

目录 1&#xff1a;什么守护线程 2&#xff1a;守护线程使用 3&#xff1a;守护线程案例 1&#xff1a;什么守护线程 守护线程是Java中的一种特殊的线程类型&#xff0c;它为其他线程&#xff08;非守护线程&#xff09;提供后台支持服务。 在Java多线程编程中&#xff0c…

蜣螂算法+四模型对比!DBO-CNN-BiLSTM-Attention系列四模型多变量时序预测

蜣螂算法四模型对比&#xff01;DBO-CNN-BiLSTM-Attention系列四模型多变量时序预测&#xff08;Matlab完整源码和数据&#xff09; 目录 蜣螂算法四模型对比&#xff01;DBO-CNN-BiLSTM-Attention系列四模型多变量时序预测&#xff08;Matlab完整源码和数据&#xff09;效果一…

服务器的维护技术都有哪些?

服务器的稳定性与可靠性是十分重要的&#xff0c;当服务器出现故障或损坏时&#xff0c;会影响业务的正常运行&#xff0c;还会导致数据丢失给企业带来巨大的经济损失&#xff0c;所以大多数的企业通常掌握着有效的服务器维护技术&#xff0c;不仅能够提高服务器的稳定性&#…

Go 语言并发编程

Go 语言的并发模型是其区别于其他编程语言的重要特性之一&#xff0c;它以简洁高效的方式解决了现代编程中多核处理器利用和高并发场景的需求。 一、并发与并行&#xff1a;概念与区别 在理解 Go 的并发模型之前&#xff0c;需要明确并发与并行的差异&#xff1a; 并发&…

基于Versoria函数优化协方差更新的改进扩展卡尔曼滤波(MVC-EKF)与经典EKF的对比,附matlab源代码|订阅专栏后可查看完整代码

本代码实现了基于Versoria函数优化协方差更新的改进扩展卡尔曼滤波(MVC-EKF),并与传统扩展卡尔曼滤波(EKF)进行对比。代码通过一维非线性运动模型仿真,展示了MVC-EKF在处理含异常值观测数据时的鲁棒性优势,适用于目标跟踪、导航定位等状态估计场景。订阅专栏后,可直接查…