当前位置: 首页 > news >正文

Go语言的单元测试与基准测试详解

文章目录

  • 单元测试
  • 基准测试

单元测试

以一个加法函数为例,对其进行单元测试。

首先编写add.go文件:

//add.go
package mainfunc add(a, b int) int {return a + b
}

其次编写add_test.go文件,在go语言中,测试文件均已_test结尾,这里只需要在被测试的文件后加上_test即可。并且测试文件与要被测试的文件需要放在同一个包中,并不像Java那样需要将所有的测试文件放在一个专门的测试文件夹里面,例如我将这两个文件都放在main包下:

package mainimport ("fmt""testing"
)//测试函数需要以Test开头
func TestAdd(t *testing.T) {fmt.Println("Running short test")res := add(1, 2)if res != 3 {t.Errorf("add(1,2) should be 3, got %d", res)}
}

cd到测试文件的目录,执行测试命令go test

以下是运行结果:

(base) PS F:\GolandProjects\GoProject1\main> go test
Running short test
PASS
ok      GoProject1/main 0.489s

如果想在测试中跳过那些需要耗时比较长的测试,可以做以下处理:

package mainimport ("fmt""testing"
)func TestAdd(t *testing.T) {fmt.Println("Running short test")res := add(1, 2)if res != 3 {t.Errorf("add(1,2) should be 3, got %d", res)}
}func TestAdd2(t *testing.T) {if testing.Short() {fmt.Println("Skipping long test")//短测试模式就跳过该测试t.Skip("Skipping long test")}fmt.Println("Running long test")res := add(5, 6)if res != 11 {t.Errorf("add(5,6) should be 11, got %d", res)}
}

在运行时指执行短测试,只需要执行go test -short

(base) PS F:\GolandProjects\GoProject1\main> go test -short
Running short test
Skipping long test
PASS
ok      GoProject1/main 0.448s

我们发现跳过了第二个测试,也就是测试函数TestAdd2

当然如果还是执行go test命令,则两个测试都将会运行:

(base) PS F:\GolandProjects\GoProject1\main> go test       
Running short test
Running long test
PASS
ok      GoProject1/main 0.417s

如果想要同时测试很多条数据,可以按如下的方式处理,而不需要写很多的函数:

func TestAdd3(t *testing.T) {var dataset = []struct {a, b, expected int}{{1, 2, 3},{5, 6, 11},{10, 20, 30},{100, 200, 300},}for _, d := range dataset {res := add(d.a, d.b)if res != d.expected {t.Errorf("add(%d,%d) should be %d, got %d", d.a, d.b, d.expected, res)}}
}

这里我们用go test -v测试一下:

(base) PS F:\GolandProjects\GoProject1\main> go test -v
=== RUN   TestAdd
Running short test
--- PASS: TestAdd (0.00s)
=== RUN   TestAdd2
Running long test
--- PASS: TestAdd2 (0.00s)
=== RUN   TestAdd3
--- PASS: TestAdd3 (0.00s)
PASS
ok      GoProject1/main 0.408s

go test 用于运行测试并显示简洁的结果,而 go test -v 用于以详细模式运行测试并提供更多的输出信息,有助于更深入地了解测试的运行情况。通常,在开发和调试过程中,使用 -v 标志是很有帮助的,但在持续集成和自动化测试中,可能更倾向于使用简洁的 go test,以便更容易解释测试结果。

基准测试

性能表现需要实际数据衡量,Go语言提供了支持基准性能测试的benchmark工具。基准测试用于确定一段代码的执行速度和性能,并可以用来优化和改进代码。

以编写斐波那契函数为例:

//fib.go
package mainfunc Fib(n int) int {if n < 2 {return n}return Fib(n-1) + Fib(n-2)
}
//fib_test.go
package mainimport ("testing"
)func BenchmarkFib10(b *testing.B) {for i := 0; i < b.N; i++ {Fib(10)}
}

benchmark 和普通的单元测试用例一样,都位于 _test.go 文件中。
函数名以 Benchmark 开头,参数是 b *testing.B。和普通的单元测试用例很像,单元测试函数名以 Test 开头,参数是t *testing.T。使用 b.N 控制循环次数:b.N 是基准测试的循环次数,它会根据不同的运行情况自动调整,以保证结果的可比性。

  • 运行当前 package 内的用例:go test .
  • 运行子 package 内的用例: go test ./<package name>
  • 如果想递归测试当前目录下的所有的 package:go test ./...

go test 命令默认不运行 benchmark 用例的,如果我们想运行 benchmark 用例,需要加上 -bench 参数。例如:

$ go test -bench .
goos: windows
goarch: amd64
pkg: GoProject1
cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
BenchmarkFib10-16        5496252               212.5 ns/op
PASS
ok      GoProject1      1.454s
  • goos: windows:这行显示运行基准测试的操作系统,此处为 Windows。
  • goarch: amd64:这行显示运行基准测试的机器架构,此处为 64 位 AMD 架构。
  • pkg: GoProject1:这行显示包含基准测试代码的包名,此处为 “GoProject1”。
  • cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz:这行显示运行基准测试的机器 CPU 信息,包括 CPU 型号和时钟频率。
  • PASS:这行表示所有的测试,包括基准测试,都已成功通过。
  • ok GoProject1 1.454s:这行显示所有测试,包括基准测试,的整体执行时间。在这种情况下,整个测试套件执行时间大约为 1.454 秒。
  • BenchmarkFib10-16 是测试函数名,-16表示GOMAXPROCS的值为16,GOMAXPROCS 1.5版本后,默认值为CPU核数 。5496252 表示一共执行5496252 次,即b.N的值。212.5 ns/op表示每次执行花费212.5ns

再举一个比较详细的例子,比较不同字符串处理方式的性能:

func Plus(n int, str string) string {s := ""for i := 0; i < n; i++ {s += str}return s
}func StrBuilder(n int, str string) string {var builder strings.Builderfor i := 0; i < n; i++ {builder.WriteString(str)}return builder.String()
}func ByteBuffer(n int, str string) string {buf := new(bytes.Buffer)for i := 0; i < n; i++ {buf.WriteString(str)}return buf.String()
}func PreStrBuilder(n int, str string) string {var builder strings.Builderbuilder.Grow(n * len(str))for i := 0; i < n; i++ {builder.WriteString(str)}return builder.String()
}
func PreStrByteBuffer(n int, str string) string {buf := new(bytes.Buffer)buf.Grow(n * len(str))for i := 0; i < n; i++ {buf.WriteString(str)}return buf.String()
}

基准测试函数:

func BenchmarkPlus(b *testing.B) {for i := 0; i < b.N; i++ {Plus(100000, "wxy")}
}func BenchmarkStrBuilder(b *testing.B) {for i := 0; i < b.N; i++ {StrBuilder(100000, "wxy")}
}func BenchmarkByteBuffer(b *testing.B) {for i := 0; i < b.N; i++ {ByteBuffer(100000, "wxy")}
}func BenchmarkPreStrBuilder(b *testing.B) {for i := 0; i < b.N; i++ {PreStrBuilder(100000, "wxy")}
}func BenchmarkPreByteBuffer(b *testing.B) {for i := 0; i < b.N; i++ {PreStrByteBuffer(100000, "wxy")}
}

以下是运行结果:

$ go test -bench .
goos: windows
goarch: amd64
pkg: GoProject1
cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
BenchmarkPlus-16                       1        1126084200 ns/op
BenchmarkStrBuilder-16              3982            284773 ns/op
BenchmarkByteBuffer-16              2947            485091 ns/op
BenchmarkPreStrBuilder-16           4771            278961 ns/op
BenchmarkPreByteBuffer-16           3310            364676 ns/op
PASS
ok      GoProject1      6.457s
  • 使用拼接性能最差,strings.Builderbytes.Buffer相近,strings.Builder更快
  • 字符串在Go语言中是不可变类型,占用内存大小是固定的
  • 使用每次都会重新分配内存
  • strings.Builderbytes.Buffer底层都是[]byte数组。内存扩容策略,不需要每次拼接重新分配内存
  • 预分配内存后,strings.Builderbytes.Buffer性能都有所提升

相关文章:

Go语言的单元测试与基准测试详解

文章目录 单元测试基准测试 单元测试 以一个加法函数为例&#xff0c;对其进行单元测试。 首先编写add.go文件&#xff1a; //add.go package mainfunc add(a, b int) int {return a b }其次编写add_test.go文件&#xff0c;在go语言中&#xff0c;测试文件均已_test结尾&a…...

【多态】为什么析构函数的名称统一处理为destructor?

析构函数的名称统一处理为destructor的目的是为了解决析构函数的重写。 而这又引出了一个问题&#xff1a;为什么要进行析构函数的重写&#xff1f; 是为了下面这种情况&#xff1a; class Person { public:~Person() { cout << "~Person" << endl; } }…...

6.4 Case Studies - A Simple Logging Archive Class

下面这段内容介绍了一个示例&#xff0c;目的是帮助澄清"归档概念&#xff08;Archive Concept&#xff09;"的用法&#xff0c;以便用户可以实现自己的归档类。simple_log_archive.hpp 实现了一个简单但实用的归档类&#xff0c;用于将任何可序列化类型以可读的格式…...

【深度学习实验】前馈神经网络(九):整合训练、评估、预测过程(Runner)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. __init__(初始化) 2. train(训练) 3. evaluate(评估) 4. predict(预测) 5. save_model 6. load_model 7. 代码整合 一、实验介绍 二、实验环境 本系列实验使用…...

002-第一代硬件系统架构确立及产品选型

第一代硬件系统架构确立及产品选型 文章目录 第一代硬件系统架构确立及产品选型项目介绍摘要硬件架构硬件结构选型及设计单片机选型上位机选型扯点别的 关键字&#xff1a; Qt、 Qml、 信号采集机、 数据处理、 上位机 项目介绍 欢迎来到我们的 QML & C 项目&#xff…...

Go基础语法:指针和make和new

8 指针、make、new 8.1 指针&#xff08;pointer&#xff09; Go 语言中没有指针操作&#xff0c;只需要记住两个符号即可&#xff1a; & 取内存地址* 根据地址取值 package mainimport "fmt"func main() {a : 18// 获取 a 的地址值并复制给 pp : &a// …...

039_小驰私房菜_Camera perfermance debug

全网最具价值的Android Camera开发学习系列资料~ 作者:8年Android Camera开发,从Camera app一直做到Hal和驱动~ 欢迎订阅,相信能扩展你的知识面,提升个人能力~ 一、抓取trace 1. adb shell "echo vendor.debug.trace.perf=1 >> /system/build.prop" 2. …...

Caché for Windows安装及配置

本文介绍在Windows上安装Cach的操作步骤。本文假设用户熟悉Windows目录结构、实用程序和命令。本文包含如下主要部分:​​​​​​ 1)Cach安装...

代码随想录算法训练营20期|第四十六天|动态规划part08|● 139.单词拆分 ● 关于多重背包,你该了解这些! ● 背包问题总结篇!

139.单词拆分 感觉这个板块要重新刷&#xff0c;完全没有印象 class Solution {public boolean wordBreak(String s, List<String> wordDict) {Set<String> set new HashSet<>(wordDict);boolean[] dp new boolean[s.length() 1];dp[0] true;for (int i…...

系统安装(一)CentOS 7 本地安装

CentOS与Ubuntu并称为Linux最著名的两个发行版&#xff0c;但由于笔者主要从事深度学习图像算法工作&#xff0c;Ubuntu作为谷歌和多数依赖库的亲儿子占据着最高生态位。但最近接手的一个项目里&#xff0c;甲方指定需要在CentOS7上运行项目代码&#xff0c;笔者被迫小小cos了一…...

obsidian使用指南

插入代码块快捷键设置 插入代码块 用英文搜索快捷键名字 英文搜索的【Insert code block】对应的是 (6个点) 中文搜索的【代码块】对应的是 &#xff08;2个点&#xff09; 查看word、excel等非md文件设置 电脑端obsidian->设置->文件与链接->检测所有类型文件->…...

【ardunio】青少年机器人四级实操代码(2023年9月)

目录 一、题目 二、示意图 三、流程图 四、硬件连接 1、舵机 2、超声波 3、LED灯 五、程序 一、题目 实操考题(共1题&#xff0c;共100分) 1. 主题&#xff1a; 迎宾机器人 器件&#xff1a;Atmega328P主控板1块&#xff0c;舵机1个&#xff0c;超声波传感器1个&…...

MYSQL的存储过程

存储过程 存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合&#xff0c;调用存储过程可以简化应用开发人员的很多工作&#xff0c;减少数据在数据库和应用服务器之间的传输&#xff0c;对于提高数据处理的效率是有好处的。存储过程思想上很简单&#xff0c;就是…...

[kubernetes/docker] failed to resolve reference ...:latest: not found

问题描述: pod一直pending, kubectl describe pod ... 显示: Warning Failed 9s (x3 over 63s) kubelet Failed to pull image "mathemagics/my-kube-scheduler": rpc error: code NotFound desc failed to pull and unpack image "docker…...

彻底解决win11系统0x80070032

经过各种尝试&#xff0c;终于找到原因。第一个是电脑加密软件&#xff0c;第二个是需要的部分功能没有开启&#xff0c;第三个BIOS设置。个人觉得第三个不重要。 解决方法 笔记本型号 笔记本型号是Thinkpad T14 gen2。进入BIOS的按键是按住Enter键。 1、关闭山丽防水墙服务…...

解决因为修改SELINUX配置文件出错导致Faild to load SELinux poilcy无法进入CentOS7系统的问题

一、问题 最近学习Kubernetes&#xff0c;需要设置永久关闭SELINUX,结果修改错了一个SELINUX配置参数&#xff0c;关机重新启动后导致无法进入CentOS7系统&#xff0c;卡在启动进度条界面。 二、解决 多次重启后&#xff0c;在启动日志中发现 Faild to load SELinux poilcy…...

flask中的跨域处理-方法二不使用第三方库

方法1(第三方库) pip install flask-cors from flask import Flask from flask_cors import CORSapp = Flask(__name__) CORS(app, resources={r"/api/*": {"origins": ["http://localhost:63342", "http://localhost:63345"]}})方…...

矿山定位系统-矿井人员定位系统在矿山自动化安全监控过程中的应用

一&#xff0c;矿井人员定位系统现阶段使用的必要性 1&#xff0c;煤矿开采是一项非常特殊的工作&#xff0c;现场属于非常复杂多变的环境&#xff0c;井下信号极差&#xff0c;数据传输非常不稳定&#xff0c;人员安全难以保证&#xff0c;煤矿企业一直在研究如何使用更合适的…...

JS-ECharts-前端图表 多层级联合饼图、柱状堆叠图、柱/线组合图、趋势图、自定义中线、平均线、气泡备注点

本篇博客背景为JavaScript。在ECharts在线编码快速上手&#xff0c;绘制相关前端可视化图表。 ECharts官网&#xff1a;https://echarts.apache.org/zh/index.html 其他的一些推荐&#xff1a; AntV&#xff1a;https://antv.vision/zh chartcube&#xff1a;https://chartcub…...

【eslint】屏蔽语言提醒

在 JavaScript 中&#xff0c;ESLint 是一种常用的静态代码分析工具&#xff0c;它用于检测和提醒代码中的潜在问题和风格问题。有时候&#xff0c;在某些特定情况下&#xff0c;你可能希望临时屏蔽或禁用某些 ESLint 的提醒信息&#xff0c;以便消除不必要的警告或避免不符合项…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...

【UE5 C++】通过文件对话框获取选择文件的路径

目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 &#xff0c;这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器&#xff0c;右键点击 .uproject 文件&#xff0c;选择 "Generate Visual Studio project files"&#xff0c;重…...