【Go系列】Go语言的测试

承上启下

        在Go语言中,我们写了代码之后经常就要进行测试。我们可以直接在go函数中调用具体的函数,从而实现测试的目的。但是一旦系统复杂的情况下,我们频繁修改main调用函数就显得不太正常了。那么是不是存在一种方法,让我们可以虚拟一些数据进行测试,并且只测试一个单一函数的功能呢?Go内置了一个test库,今天我们学习一下这个库怎么使用把。

开始学习

在Go语言中,测试是开发过程的一个重要组成部分。Go内置了一套强大的测试框架,使得编写单元测试和基准测试变得简单快捷。以下是关于Go语言测试的介绍,包括testing库和基准测试(benchmark)。

测试文件命名规范

Go语言的测试文件通常与被测试的代码放在同一包中,并且文件名以_test.go结尾。例如,如果我们要为math包中的add.go文件编写测试,测试文件应该命名为add_test.go

单元测试

什么是单元测试

        单元测试是指对软件中的最小可测试部分进行检查。在面向对象编程中,这通常指的是单个方法或函数;在过程式编程中,则可能是一个过程或函数。单元测试的目标是验证这个单元是否正确执行了预定的任务,并且没有意外的副作用。

单元测试的步骤

单元测试用于验证代码的各个部分是否按预期工作。以下是编写单元测试的基本步骤:

  1. 导入testing:所有测试代码都需要导入testing包。

  2. 编写测试函数:测试函数的名称必须以Test开头,并且接受一个类型为*testing.T的参数。

  3. 使用t.Errorft.Fatalf来报告错误:如果测试未通过,可以使用这些方法来记录错误信息。

以下是一个简单的单元测试示例:

package mainimport "testing"// add 是被测试的函数
func add(a, b int) int {return a + b
}// TestAdd 是 add 函数的单元测试
func TestAdd(t *testing.T) {result := add(1, 2)if result != 3 {t.Errorf("add(1, 2) = %d; want 3", result)}
}

要运行测试,可以在命令行中使用go test命令。

基准测试(Benchmark)

什么是基准测试

基准测试(Benchmark Testing),也称为性能测试,是一种测量和评估软件或硬件性能的测试方法。在软件开发中,基准测试通常用于评估代码片段或算法的运行效率,包括执行速度、内存使用、吞吐量等性能指标。

基准测试的主要目的是:

  • 评估性能:确定代码或系统在特定条件下的性能表现。
  • 性能优化:通过比较不同实现或配置的性能,帮助开发者进行优化。
  • 性能监控:监控软件或系统性能随时间的变化,及时发现潜在的性能问题。

基准测试的步骤

基准测试用于衡量代码的性能,特别是执行速度。以下是编写基准测试的基本步骤:

  1. 导入testing

  2. 编写基准测试函数:基准测试函数的名称必须以Benchmark开头,并且接受一个类型为*testing.B的参数。

  3. 使用b.N来重复执行测试代码b.N是基准测试框架提供的,表示测试应该执行的次数。

以下是一个基准测试的示例:

package mainimport "testing"func BenchmarkAdd(b *testing.B) {for i := 0; i < b.N; i++ {add(1, 2)}
}

要运行基准测试,可以在命令行中使用go test -bench=.命令。

子测试和子基准测试

Go 1.7+ 引入了子测试和子基准测试的概念,允许在同一个测试函数中运行多个测试或基准测试。

func TestAdd(t *testing.T) {tests := []struct {a, b, want int}{{1, 2, 3},{2, 3, 5},{-1, -1, -2},}for _, tt := range tests {t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {if got := add(tt.a, tt.b); got != tt.want {t.Errorf("add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)}})}
}

覆盖率

Go还提供了代码覆盖率工具,用于衡量测试执行了多少代码。可以使用go test -cover来获取覆盖率。

计时方法

基准测试通常关注代码执行的时间。以下是几种常见的计时方法:

  1. Wall-time 计时:测量实际经过的时间,即从测试开始到结束的总时间。

  2. CPU-time 计时:测量处理器执行测试代码所花费的时间,不包括等待I/O操作或其他系统活动的时间。

  3. 用户-time 和系统-time:用户-time 是指程序在用户模式下执行所花费的时间,系统-time 是指程序在内核模式下执行所花费的时间。

我们同样可以通过ResetTimer计时器的方法来进行计时。

假设我们有一个简单的函数calculate,它执行一些计算操作:

package mainimport ("testing""time"
)// calculate 是一个简单的计算函数,用于基准测试
func calculate(n int) int {sum := 0for i := 0; i < n; i++ {sum += i}return sum
}// BenchmarkCalculate 是 calculate 函数的基准测试
func BenchmarkCalculate(b *testing.B) {// 重置计时器b.ResetTimer()// 运行基准测试for i := 0; i < b.N; i++ {calculate(1000)}// 停止计时器b.StopTimer()// 报告内存分配情况b.ReportAllocs()
}// 测试命令: go test -bench=.

在这个基准测试中,我们使用了以下计时方法:

  1. b.ResetTimer():在开始执行基准测试之前重置计时器。
  2. b.StopTimer():在完成基准测试后停止计时器。

这两个方法确保我们只测量calculate函数执行的时间,而不包括设置测试环境所需的时间。

现在,让我们运行基准测试并查看结果:

go test -bench=Calculate -benchmem

输出可能类似于以下内容:

goos: darwin
goarch: amd64
pkg: example
cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz
BenchmarkCalculate-4    1000000              1060 ns/op             0 B/op          0 allocs/op
PASS
ok      example    1.509s

在这个输出中:

  • BenchmarkCalculate-4 表示基准测试的名称和使用的CPU核心数。
  • 1000000 是基准测试中执行的迭代次数(b.N)。
  • 1060 ns/op 表示每次操作的平均时间(纳秒)。
  • 0 B/op 表示每次操作的平均内存分配量(字节)。
  • 0 allocs/op 表示每次操作的平均内存分配次数。

这个示例展示了如何在Go中进行基本的计时和内存统计。通过-benchmem标志,我们可以获得内存分配的详细信息。

内存统计

内存统计是基准测试中另一个重要的方面,它帮助开发者了解代码执行过程中的内存使用情况。以下是内存统计的一些关键点:

  1. 内存分配次数:代码执行过程中发生的内存分配次数。

  2. 内存分配量:代码执行过程中总共分配的内存量。

  3. 内存使用峰值:代码执行过程中内存使用的最高点。

在Go语言中,可以使用 testing 包提供的 BenchmarkResult 结构来获取内存统计信息,如下所示:

func BenchmarkFib(b *testing.B) {// ... 测试代码 ...result := testing.Benchmark(b.run)b.ReportAllocs() // 报告内存分配情况// 可以通过 result.AllocedBytesPerOp 等字段获取内存统计信息
}

并发基准测试

并发基准测试用于评估代码在多线程或多进程环境下的性能。以下是并发基准测试的一些关键点:

  1. 并发的级别:测试中同时运行的goroutine数量。

  2. 同步机制:在并发测试中,需要考虑同步机制(如锁、通道等)对性能的影响。

  3. 竞争条件:并发测试可以揭示潜在的竞争条件或死锁问题。

在Go语言中,可以使用 runtime 包来控制并发基准测试的并发级别,如下所示:

func BenchmarkConcurrentFib(b *testing.B) {// 设置并发级别numGoroutines := runtime.NumCPU()b.SetParallelism(numGoroutines)b.RunParallel(func(pb *testing.PB) {for pb.Next() {fib(10) // 并发执行 fib(10)}})
}

在这个例子中,SetParallelism 设置了基准测试的并发级别,而 RunParallel 则用于执行并发的基准测试。

总结

Go语言的测试框架简单而强大,通过testing包,开发者可以轻松编写单元测试和基准测试来确保代码的正确性和性能。以下是一些最佳实践:

  • 测试文件应该与被测试的代码放在同一包中。
  • 测试函数的名称应该以TestBenchmark开头。
  • 使用子测试和子基准测试来组织相关的测试用例。
  • 定期运行基准测试来监控性能变化。
  • 使用覆盖率工具来确保测试覆盖了足够多的代码路径。

通过遵循这些实践,可以确保Go项目的质量和性能。

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

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

相关文章

代码随想录学习 54day 图论 Bellman_ford 队列优化算法(又名SPFA) 学习

Bellman_ford 队列优化算法&#xff08;又名SPFA&#xff09; 卡码网&#xff1a;94. 城市间货物运输 I 题目描述 某国为促进城市间经济交流&#xff0c;决定对货物运输提供补贴。共有 n 个编号为 1 到 n 的城市&#xff0c;通过道路网络连接&#xff0c;网络中的道路仅允许从…

Hadoop3:RPC通信原理及简单案例实现

一、场景介绍 我们知道&#xff0c;Hadoop中存在多种服务&#xff0c;那么&#xff0c;服务之间是如何通信的了&#xff1f; 比如&#xff0c;DN和NN之间如何通信&#xff1f; 这里&#xff0c;实际上是通过RPC实现进程间通信的了。 RPC属于Java网络编程范畴 需要编写客户端和…

自用自用自用,持续更新,记录部分CPU,显卡,部分跑分软件,游戏帧数参考,自用

自用自用自用&#xff0c;持续更新&#xff0c;记录部分CPU&#xff0c;显卡&#xff0c;部分跑分软件&#xff0c;游戏帧数参考&#xff0c;自用 CPU跑分显卡游戏 CPU跑分 CPUZ单核CPUZ多核R23单核R23多核5800h576.85860.014311270212100f644.43258.31576804313500h763.36658…

AAD Connect自定义同步用户上云

使用场景&#xff1a;我想同步本地AD域的那些用户信息、账号上云端做SSO登录和权限管控&#xff0c;但是不希望使用快速上传一股脑传上去&#xff0c;所以使用自定义同步功能上传&#xff0c;这是一篇对AAD CONNECT这个应用的详解和配置步骤推荐 AD Connect如何自定义配置&…

easyswoole/Hyperf开发的php系统 cpu超负荷定位排查

EasySwoole EasySwoole是一个高性能的PHP协程框架&#xff0c;它利用了协程的特性来提高PHP应用的性能。当使用EasySwoole开发的PHP系统遇到CPU超负荷的问题时&#xff0c;可以从以下几个方面进行全方位排查和优化&#xff1a; 1. 监控系统资源 使用top、htop、vmstat、iost…

隐性行为克隆——机器人的复杂行为模仿学习的新表述

介绍 论文地址&#xff1a;https://arxiv.org/pdf/2109.00137.pdf 源码地址&#xff1a;https://github.com/opendilab/DI-engine.git 近年来&#xff0c;人们对机器人学习进行了大量研究&#xff0c;并取得了许多成果。其中&#xff0c;模仿学习法尤其受到关注。这是一种从人…

iOS ------ 消息传递和消息转发

一&#xff0c;消息传递 在OC中&#xff0c;传递消息就是在对象上调用方法。 相对于C语言的方法就“静态绑定”的函数&#xff0c;在编译器就决定了运行时所要调用的函数。在OC中&#xff0c;如果向某对象传递消息&#xff0c;就会使用动态绑定机制来决定需要调用那个方法。调…

全球风味:红酒中的地域风情与特色

在红酒的世界里&#xff0c;每一滴琼浆玉液都承载着地域的风情与特色。它们不仅仅是葡萄酒&#xff0c;更是大自然的恩赐&#xff0c;是时间的馈赠&#xff0c;是人类智慧的结晶。今天&#xff0c;就让我们一起走进红酒的世界&#xff0c;感受那些来自不同地域的风情与魅力。 …

前端面试题日常练-day91 【Less】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末 在Less中&#xff0c;以下哪种语法适用于创建混合器&#xff08;Mixin&#xff09;&#xff1f; a) mixin b) #mixin c) .mixin d) extend Less中的子元素选择器是用什么符号表示的&#xff1f; a) &…

【Vue】RouterLink的replace属性

1、作用&#xff1a;控制路由跳转时操作浏览器历史记录的模式&#xff1b; 2、浏览器的历史记录有两种写入方式&#xff1a;分别为push和replace&#xff0c;push是追加历史记录&#xff0c;replace是替换当前记录。路由跳转时候默认为push&#xff1b; 3、如何开启replace模式…

ROS2入门到精通—— 2-6 ROS2实战:可调节纯跟踪算法(局部规划)

1 Regulated Pure Pursuit 纯追踪算法变体&#xff1a;调节纯追踪算法 将自适应纯追踪&#xff08;Adaptive Pure Pursuit&#xff09;算法的特性与围绕线性速度的规则相结合&#xff0c;重点关注消费类、工业和服务型机器人的需求。我们还实现了几种常识性的安全机制&#xf…

业务终端动态分配IP-DHCP技术、DHCP中继技术

一、为什么需要DHCP? 1、许多设备(主机、无线WiFi终端等)需要动态地址的分配; 2、人工手工配置任务繁琐、容易出错,比如:IP地址冲突; 3、网络规模扩大、复杂度提高,网络配置越来越复杂,计算机的位置变化和数量超过可分配IP地址的数量,造成IP地址变法频繁以及IP地址…

Monaco 使用 DocumentHighlightProvider

Monaco 中有一个文字高亮的功能&#xff0c;就是选中一个单词&#xff0c;会高亮文字文档中所有出现该单词的位置&#xff0c;效果如下&#xff1a; Monaco 默认就有这个功能&#xff0c;可以根据具体需求进行定制。通过 registerDocumentHighlightProvider 进行注册 实现 pro…

java包装类 及其缓存

Java 包装类&#xff08;Wrapper Class&#xff09;是将基本数据类型转换为对象的方式&#xff0c;每个基本数据类型在 java.lang 包中都有一个相应的包装类&#xff1a; Boolean 对应基本类型 boolean Character 对应基本类型 char Integer 对应基本类型 int Float 对应基本…

Java时间练习(8) (2024.7.17)

Duration、Period、ChronoUnit类 package DurationPeriodChronoUnitExercise20240717; import java.time.*; import java.time.temporal.ChronoUnit;// ChronoUnit是用来得到时间间隔的类&#xff0c;涵盖了所有时间的单位&#xff0c;Duration和Period用法和其一致&#xff0…

【Java数据结构】初始线性表之一:链表

为什么要有链表 上一节我们描述了顺序表&#xff1a;【Java数据结构】初识线性表之一&#xff1a;顺序表-CSDN博客 并且进行了简单模拟实现。通过源码知道&#xff0c;ArrayList底层使用数组来存储元素。 由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者…

代码随想录二刷复习(二分法)

二分法模板&#xff1a; 1&#xff1a;左闭右闭区间写法 第一种写法&#xff0c;我们定义 target 是在一个在左闭右闭的区间里&#xff0c;也就是[left, right] &#xff08;这个很重要非常重要&#xff09;。 区间的定义这就决定了二分法的代码应该如何写&#xff0c;因为定…

vue 给特定满足条件的表单数据添加背景颜色,组件的 row-class-name

1、:row-class-name"tableRowClassName" 可为表格每行根据后面的函数绑定class名 <!-- 列表框 --><div class"tableList"><el-table :data"teamModelListTable" style"width: 100%"selection-change"handleSele…

Java中的限定符和基本数据类型

限定符和数据类型 1、限定符 限定符一般指用于改变类、方法、变量等成员行为的关键字&#xff0c;这里分为访问限定符和非访问限定符。 访问限定符 访问范围privatedefaultprotectedpublic同一类1111同一包111&#xff08;其他包&#xff09;子类11全局1 非访问限定符 final&…

el-table表格操作列错行处理

解决方法&#xff1a; <style>::v-deep .el-table th.el-table__cell > .cell {white-space: nowrap !important;} </style>