go 基准测试 找不到函数_基于Golang做测试

本文在实习期间完成并完善,无任何公司机密,仅做语言交流学习之用。

持续更新。


1.Golang的单元测试

Go语言提供了丰富的单测功能。在Go中,我们通常认为函数是最小的可执行单元。本例中使用两个简单的函数:IsOdd和IsPalindrome来进行Go单测的研究。

171457c245794c2982c3088953e5848f.png

在VSCode中,在函数名上点击右键,选择“Go: Generate Unit Tests For Function"即可生成单测文件。

2461b13ac0aa1ebea157539ad8454d1a.png

前往对应的*_test.go,开始以表格化的方式填写测试用例。这里我们每个函数都填5个测试用例:

87616b397052867a8991987dc7abbb20.png

这里name代表的是测试用例的名字,这里建议每个测试用例的名字都唯一,否则你很有可能不知道发生错误的用例到底是哪个。同时我在34行和38行加入了当测试用例不通过时,输出测试用例名字的语句。这样就可以快速定位到测试不通过的测试用例。这里我们故意将第5个测试用例的want写错(32767是奇数,所以本应该返回true的,也就是want应该为true),看看测试工具是怎么报错的。

点击函数名上面那个run test,,这样可以开始执行测试。run test只会运行它所下面一行所声明的测试函数。如果需要测试所有函数可以命令行输入go test或者go test -v。对IsOdd的测试如下:

a72705322326a66bd35c49fe31eb5dae.png

可以看到其实不自己添加用例输出语句,FAIL的时候go的测试工具也会帮你输出到底是哪个用例不通过。所以34行和38行的代码并不是必须的。我们把第五个测试用例修改成正确的,再次运行测试:

140a3dfa4a67b9e9d654ce1a58239245.png

这个时候全部的测试用例就通过了,而且因为这是一种完全只是关心输入输出的测试,并不涉及到函数内部的具体细节,我们称之为“黑盒测试”。

而其中我们还能选择不同的日志等级输出错误信息,单测框架提供的所有日志方法都会结束测试,若只想标记测试用例不通过,请使用t.Fail()。具体的日志方法如表:

方法功能

t.Log()/t.Logf() 打印标准等级日志,同时结束测试

t.Error()/t.Errorf() 打印错误等级日志,同时结束测试

t.Fatal()/t.Fatalf() 打印致命等级日志,同时结束测试

2.Golang的测试覆盖率

Go的测试覆盖率一般指的是测试用例可以触发函数内的多少个分支语句占全部的分支语句的比例,在VSCode中可以以颜色区分的方式来判断当前的测试函数覆盖到了哪些分支,没有覆盖到哪些分支。首先,在测试文件中,右键任意一个测试函数,选择"Go: Toggle Test Coverage In Current Package"来开始进行测试样例覆盖。

39ae399815d1b4e1f03f3dc1373dea27.png

这里我们将返回值应为false的测试用例给去掉,执行测试。测试完成后回到被测试的函数源文件中,可以发现被测到的分支为以墨绿色标记,而没有被测试到的代码分支以红色标记。

77f5455e636f2c3d32e75ba8f6841e8a.png

将测试样例复原后再进行测试,可以发现测试覆盖率达到了100%,同时所有的代码都是墨绿色的了:

1394b972807082566850f56a510492fa.png

3.Golang的基准性能测试

3.1 非并行Golang benchmark

Golang提供了测试函数运行性能的工具,对于所有的函数来说,其性能测试函数都是在前面加Benchmark。我们还是用上面所说的两个函数,来写一下它们的benchmark测试函数(但是我没找到怎么一键生成benchmark的选项):

d921feb916e4bcc3078735bbc32cbca8.png

其中b.ReportAllocs()会报告这个函数的内存使用情况(执行一次方法要申请多少次内存,每次申请需要申请多大的内存),也可以通过指定-benchmem参数来输出所有函数的内存性能。

执行测试命令(或者使用VSCode的那个run benchmark按钮测试单个函数的benchmark):

Linux:
go test -bench=.
go test -bench=. -benchmem # If memory analysis info is required
go test -bench=. -benchtime=3s # If benchmark test time is not 1s, use -benchtime to set it
Windows:
go test -bench="."
go test -bench="." -benchmem # If memory analysis info is required
go test -bench="." -benchtime=3s # If benchmark test time is not 1s, use -benchtime to set it

测试出来的结果如下:

81b4ce139bfed07ebf010a8d645e31b7.png

输出解读:

数据意义

BenchmarkIsOdd-8 以P=8来测试IsOdd的性能

232279942 代表在1s内(如果没有指定-benchtime则默认测试时间为1s)执行了IsOdd 232279942次

5.16 ns/op 代表每执行一次IsOdd所花费的时间为5.16 ns

0B/op 代表每执行一次IsOdd所分配的内存为0B

0 allocs/op 代表每执行一次IsOdd申请分配内存的次数为0次

我们新写一个函数,这个函数涉及到分配内存。我们先写一个AllocFixedArray来申请一个长度固定的数组并循环往里面填写数据,然后再写一个AllocMutableArray来申请一个长度可扩充的数组,使这两个申请数组的长度相同,观察它们的性能:

2e383b19dd4303696eb97b5d49d0ec7a.png

首先对它们做单测,确保代码运行上没有问题,由于这里没有逻辑判断,所以有一个测试用例就够了:

5442d4e1692fef2f9a0015af15f399d9.png

确认结果正确后,写出它们对应的Benchmark函数:

0c80664bb240707734343e2349fe5863.png

然后可以点击run benchmark一个个测,或者直接全部函数都测一下,这里选择全部测试:

e1d23cf8cdfcb8445544a31d7abbd31f.png

可以发现,使用make声明固定长度的内存是没有allocs的,而append底部会在数组长度不足的时候对数组进行扩充,所以会有内存的申请。并且我们可以看到,append因为底层申请了内存,性能大大下降,AllocMutableArray的执行时间是AllocFixedArray的差不多25倍。这个也提示我们,尽量要对数组的大小有一个预先的估计,并申请好一个capacity比较接近最大上限的数组。

3.2 并行Golang benchmark

测试的时候同样可以使用并行的方法去并发测试指定的时间内能执行多少次该方法,其基本语法为:

b

我们试试将执行比较慢的AllocMutableArray()来并发处理,看看会如何:

822e99bde21d6c722f48c72cff1c11ea.png

执行基准测试,得到结果:

10585c6c2473519138311fba014b8661.png

我们可以发现,在P=8并发执行AllocMutableArray之后,执行时间从73.6ns/op降到了14.6ns/op。

3.3 Golang benckmark中的计时器

假设说一个函数在执行之前,要先执行一些外部的初始化操作。而我们如果在go test里面制定了-benchtime选项,它记录的将会是整个Benchmark函数的运行时间。所以我们需要有一种操纵定时器的方法,来获得整个服务精确的运行时间。假设我们的IsOdd,它在执行之前需要睡眠100毫秒,那么我们就可以在执行完睡眠之后,使用重置计数器的方法开始计时。

d4b66acb8a78770e0a7750d536a047d3.png

a5af15f2135762a807f58fdc7c9203a8.png

OK,测试用的总共时间为3.166s。然后我们加上不对初始化进行计时的代码(取消16、18行的注释),重新测一次:

2e5b0fe12ed7368c8ce620ded6174150.png

可以看到测试时间有显著的下降,这说明使用b.StopTimer()后,没有将初始化的时间算在总测试时间内。

方法功能

b.StartTimer() 复原或打开计时器,当Benchmark执行前会首先执行b.StartTimer()

b.StopTimer() 暂停当前计时器

b.ResetTimer() 重置当前计时器的值,go官方说该函数在计时器运行时无效,但我试了一下是有效的。建议先b.StopTimer()后再调用此方法,最后再b.StartTimer()

StartTimer, StopTimer和ResetTimer其实就相当于我们常用的秒表的三个按钮:开始,暂停和复位。当Benchmark函数执行之前,就会自动调用StartTimer。而ResetTimer函数生效的前提是必须先调用StopTimer。通过这样的控制,就可以控制基准测试的计时器,防止一些无关部分的时间被测算进来了。

3.4 Golang benchmark的Profile(性能分析)

golang的benchmark提供了一种输出性能分析的工具,在测试benchmark命令的前提下,加上参数即可,下面提供了三种获取全部基准测试函数不同性能的指令:

test -bench

当获得这些性能文件之后,也会相应地留下一个***.test为文件名的可执行文件。为什么要留下这个测试时候生成的临时程序呢?在生成profile文件的时候,为了减少冗余,生成的文件全部都是不含符号信息的,也就是说其实并没有记录性能条目对应的是哪个函数的性能,所以需要有一个这样的副本程序来记录符号信息。

当我们获得这些文件之后,使用go自带的pprof来查看这些文件所表示的含义,其中-nodecount=10表示仅显示前10个最耗性能的条目:

=

其中***为根据实际需要所替换的字符串,一个名为go-learning的程序的cpu占用情况分析如下图:

da18e46f45e01db6f52cfb588785f7d7.png

可以看到,IsPalindrome的占用时间排第2位,仅次于gc。所以我们可以着手去从这个函数进行优化。

4.Golang的Example测试(样例测试)

样例测试比较像平时在一些算法刷题平台(比如LeetCode)的题目的一些例子,样例测试以Example打头,其逻辑也很简单,就是使用fmt.Println输出该测试用例的返回结果,然后在函数体的末尾使用如图的注释,一一对应每个fmt.Println的输出:

0c5826a68c02445e512d3dcbdaa5d56d.png

17行的output首字母大写小写均可。

如果13~16行输出的结果和18~21行的结果相对应,go test就会PASS,否则就会FAIL,并打印出实际输出和期望输出。

5.Go的Mock方法

5.1 Mock的简介

mock,中文译名为“模仿,假的”,顾名思义就是构建一个模拟对象,来替换掉一些需要在特定环境下触发的服务,使其可以在不修改原服务的前提下达到测试的目的。本文介绍一种是基于gomonkey的函数/变量Mock方法。

5.2 基于gomonkey的函数Mock方法

在使用gomonkey之前,我们要先安装它,输入命令:

go get github.com/agiledragon/gomonkey

并在开头import该包:

import 

假设我们有一个函数IsRest,当调用这个函数的时候,程序会判断一下现在的时间是否已经是下午5点之后,如果是,就返回nil,表示现在是下班时间了。否则返回非nil值,表示现在还没到下班时间。我们先写出这个函数:

d1e4ba6228cd6b25e724b1f5e34187b5.png

那我们测试的时候肯定不可能等到5点再去测这个函数吧?否则这测试不就没法做了。这个时候我们先生成它的单测函数,然后施加mock:

61b54abe756b11b88cd6fea80aea9009.png

其中,108行~114行是对IsRest进行mock的方法,ApplyFunc指的是对函数进行Mock,第二个参数就是要使用的Mock方法。

那我们来执行测试:

42b0589bc9e8925c039732484dda35bc.png

说明在执行测试用例的时候,gomonkey成功地把IsRest方法给mock掉了。

6. 总结

Go语言本身提供了丰富的单元测试和性能测试方法,但是在提供Mock方法上还是略有不足。本文从Gomock, Gomonkey和GoStub出发,总结了一些创建Mock对象的方法。如果对于Go测试有进一步兴趣的,可以去了解GoConvey,GoMonkey,GoStub和GoMock的教程,下面列出了一个作者写的关于这四个测试工具的文章,供读者参考:

GoConvey框架使用指南

https://www.jianshu.com/p/633b55d73ddd​www.jianshu.com

GoMock框架使用指南

https://www.jianshu.com/p/f4e773a1b11f​www.jianshu.com

GoStub框架使用指南

https://www.jianshu.com/p/70a93a9ed186​www.jianshu.com

Monkey框架使用指南

Monkey框架使用指南​www.jianshu.com
ae644f7272f8893f28bb044a798dd64f.png

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

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

相关文章

九齐NY8B072A单片机使用笔记(三)模拟串口RX

因为这款单片机没有硬件串口,所以需要我们自己做软件模拟串口。 用PA3作为RX,因为PA3可以作为外部输入中断EXTI1。 本人首先用轮询的方式查PA3是否从高电平跳变到低电平(起始信号),但是因为还有别的业务逻辑&#xf…

[LindCode] Binary Tree Postorder Traversal

Binary Tree Postorder Traversal Given a binary tree, return the postorder traversal of its nodes values. Example Given binary tree {1,#,2,3}, 1\2/3return [3,2,1]. Challenge Can you do it without recursion? SOLUTION 1: recursion: 分治法解决之&am…

金山毒霸垃圾清理

金山毒霸-垃圾清理-单文件封装,清洁洁癖的爱好! 实话,金山的软件确实不错。展望金山可以在软件行业,做出让世界都使用的。为国人扛起一片天 下载地址: http://pan.baidu.com/s/1dFa7GdV转载于:https://www.cnblogs.com/xiaochina/…

python-break循环中断

Python break语句,就像在C语言中,打破了最小封闭for或while循环。 break语句用来终止循环语句,即循环条件没有False条件或者序列还没被完全递归完,也会停止执行循环语句。 break语句用在while和for循环中。 如果您使用嵌套循环&am…

asp.net+mvc+easyui+sqlite 简单用户系统学习之旅(二)—— easyui的简单实用

下面开始在UserManager.Web中利用easyUI构建web。 1. 先删除自带的controllers、models和views(里面的shared和web.config可以保存)下面的文件 2. 要利用easyUI,首先去网上下载jquery-easyui-1.3.2.zip,同时下载一份EasyUI-1.3.2.…

adc如何获取周期_LOL:千珏拥有ADC最需要的位移和无敌能力,为什么没人用她打下路?...

— 点击蓝字 关注我们 —英雄联盟自国服上线以来,已经陪伴玩家走过了9个年头,目前英雄联盟中的英雄数量已经达到了151位,每一位都各具特色。千珏是一位深受玩家们喜爱的英雄,其在官方英雄的定位中,属于打野英雄&#x…

android surfaceview 大小_Android 使用Camera2 API采集视频数据

Android 视频数据采集系列的最后一篇出炉了,和前两篇文章想比,这篇文章从系统API层面进行一些探索,涉及到的细节更多。初次接触 Camera2 API 会觉得它的使用有些繁琐,涉及到的类有些多,不过就像第一次使用Activity, Fr…

使用Java VisualVM分析您的应用程序

当您需要发现应用程序的哪个部分消耗更多的CPU或内存时,必须使用探查器执行此操作。 默认情况下,Sun JDK中附带的一个探查器是Java VisualVM。 这个事件探查器非常简单易用,功能强大。 在这篇文章中,我们将看到如何安装它并使用它…

ArcSDE for SQL Server安装及在ArcMap中创建ArcSDE连接

ArcSDE for SQL Server安装及在ArcMap中创建ArcSDE连接 原文:ArcSDE for SQL Server安装及在ArcMap中创建ArcSDE连接安装ArcSDE for SQL Server,最后一步成功后的界面如下:在ArcMap中创建ArcSDE连接,截图如下:posted on 2016-08-0…

python调用c函数传字符串参数_Python使用ctypes模块调用DLL函数之传递数值、指针与字符串参数...

在Python语言中,可以使用ctypes模块调用其它如C语言编写的动态链接库DLL文件中的函数,在提高软件运行效率的同时,也可以充分利用目前市面上各种第三方的DLL库函数,以扩充Python软件的功能及应用领域,减少重复编写代码、…

沁恒CH554 KEIL环境搭建

首先下载WCHISPTool_Setup.exe http://www.wch.cn/products/CH554.html 123这三个可下载的都下吧,后面开发都要用的 安装好后运行,菜单栏上,功能->添加WCH MCU到KEIL器件库 这时候在KEIL安装目录里面的UV4文件夹下可以看到wch.cdb的文件…

【CV论文阅读】Rank Pooling for Action Recognition

这是期刊论文的版本,不是会议论文的版本。看了论文之后,只能说,太TM聪明了。膜拜~~ 视频的表示方法有很多,一般是把它看作帧的序列。论文提出一种新的方法去表示视频,用ranking function的参数编码视频的帧序列。它使用…

VS2019 WPF制作OTA上位机(一)新建工程

首先创建新项目,文件 -> 新建 -> 项目 下拉菜单选择C#和Window,选择WPF应用程序,下一步 输入项目名,下一步 这里选择.NET 5.0,也可以选择其他的,个人习惯.NET,点击创建 这时候出现初始…

户籍恢复需要体检吗_脑梗死后脚麻能恢复吗?需要多久能恢复呢?

脑梗死之后脚部麻木,这个有一部分是能够恢复的,但是相对而言,恢复的时间比较长,在临床当中出现脚麻主要是因为梗死破坏了患者的感觉神经中枢从而造成。脚部感觉麻木,瘙痒或者是有蚂蚁在上面爬的感觉。而且有的更加严重…

Alpha版本测试报告

一、测试计划 Alpha版本即将发布,我们组织队员进行这一版本的测试。 测试主要针对两方面:浏览器兼容性和功能完善性。 测试分兼容性测试与功能完善性两部分,兼容性测试分Windows操作系统、Linux系操作系统、Mac OS X操作系统以及手机端Androi…

VS2019 WPF制作OTA上位机(二)获取bin文件路径

OTA升级是通过无线通信远程把bin文件内容传输到单片机,完成升级。 因此上位机需要获取bin文件的路径,读取bin文件内容,将内容分割依次发送(因为单片机的接收缓存不会开得和bin文件一样大(十几K甚至几十K)&a…

linux更改用户名_破旧安卓手机第二春,在安卓手机上使用Linux_deploy运行Linux

由于服务器位于国外,害怕被墙掉导致数据丢失,所以在本地写了脚本每小时从服务器上导出并下载到本地。但是电脑不可能二十四小时开机,所以很想买一个树莓派4玩玩。但是太贵辽,还好搜索到了Android运行Linux的方法,下面记…

MCUXpress IDE常用设置

NXP的开发工具Xpress是基于eclipse制作的,我们如果需要设置一些东西可以直接搜索eclipse是怎么设置的。 1、字体大小 搜索eclipse字体大小,菜单Window > Preference 而Xpress是汉化了的,英语不好的同学可能懵逼,其实就是菜单栏…

C语言中空格符、空字符、字符数组结束符、换行、回车的区别

空格符和空字符是不一样的,在ASCII里面,空格(space)符号的ASCII码是32,而空字符是0, 2个是完全不一样的2个字符 空字符 一般来描述一个字符串的结尾,其实是控制符的一种,但不能理解为没有字符,应该理解为代表什么都没有的字符.好比回车0x0A和换行0x0D虽然不显示,但是也是控制字…

anaconda如何卸载库_小白必看!Anaconda安装全攻略

本文作者:戴 雯文字编辑:方 言技术总编:张馨月爬虫俱乐部云端课程来袭!爬虫俱乐部将于2020年8月25日至28日在线上举行Stata数据分析法律与制度专题训练营,主要是为了让学员掌握Stata软件进阶操作,涉及…