Go语言学习17-功能测试

2023-11-01

引言

Go语言中提供了 go test 命令,它不仅仅可以对代码包进行测试,还可以对个别源码文件进行测试,只要存在针对这些测试的测试源码文件。除此之外,Go语言还在标准库中提供了一个专门用于测试的代码包 testing,它提供了编写测试源码文件所需的一切。

功能测试

1. 编写功能测试函数

测试源码文件总应该与被它测试的源码文件处于同一代码包内。在编写测试源码文件的时候,总是会用到标准库代码包 testing 中的 APItesting 包为Go语言的代码包提供了自动化测试支持。它的目标是与 go test 命令协同使用,以自动执行目标代码包中的任何测试函数。

在测试源码文件中,针对其他源码文件中的程序实体的功能测试程序总是以函数为单位的。被用于测试程序实体功能的函数的名称和签名形如:

func TestXxx(t *testing.T)

其中,Xxx 应该是大写字母开头的若干字母或数字的组合,通常情况下会将 Xxx 替换成被测试的程序实体的名称。可以利用 *testing.T 类型的参数 t 上的一些方法对功能测试的过程进行记录和控制。使用 t 的值上的方法记录的信息会在测试结束之后(不论成败),一并打印到标准输出上。

2. 常规记录

参数 t 上的 LogLogf 方法一般用于记录一些常规信息,以展示测试程序的运行过程以及被测试程序实体的实时状态。调用语句如下:

t.Log("Tomorrow is a ", "good ", "day ") // 类似于fmt.Println 
t.Logf("Tomorrow is a %s", " good day ") // 类似于fmt.Printf

使用 go test –v 命令,两者都会打印如下信息:

xxx_test.go:10: Tomorrow is a good day
xxx_test.go:11: Tomorrow is a good day
打印信息 解释
xxx_test.go 调用语句所在的测试源码文件的 名称
10 调用语句出现的 行号

3. 错误记录

参数 t 上的 ErrorErrorf 方法被用于记录错误信息。当被测试的程序实体的状态不正确的时候,使用 t.Errort.Errorf 方法,及时对当前的错误状态进行记录。例如:

actLen := len(s)
if actLen != expLen {
	t.Errorf("Error: The length of slice should be %d but %d.\n", expLen, actLen)
}

调用 t.Error 方法相当于先后对 t.Logt.Fail 方法进行调用,而调用 t.Errorf 方法则相当于与先后对 t.Logft.Fail 方法进行调用。

4. 致命错误记录

参数 t 上的 FatalFatalf 方法被用于记录致命的程序实体的状态错误。所谓致命错误是指使得测试无法继续进行的错误。例如:

if listener == nil {
	t.Fatalf("Listener startup failing! (addr=%s)!\n", serverAddr)
}

调用 t.Fatal 方法相当于先后对 t.Logt.FailNow 方法进行调用,而调用 t.Fatalf 方法则相当于先后对 t.Logft.FailNow 方法进行调用。

5. 失败标记

如果需要标记当前测试函数中的测试是失败的,那么就需要用到 t.Fail 方法。对 t.Fail 方法的调用不会终止当前测试函数的执行。但是,此函数的测试结果已经被标记为失败了。

6. 立即失败标记

方法 t.FailNowt.Fail 不同的地方是,它在被调用时会立即终止当前测试函数的执行。这会使得当前的测试运行程序转而去执行其他的测试函数。

注意: 只能在运行测试函数的 Goroutine 中调用 t.FailNow 方法,而不能在测试代码创建出的 Goroutine 中调用它。不过,在其他的 Goroutine 中调用 t.FailNow 方法也不会造成什么错误,只是它不会产生任何效果而已。

7. 失败判断

在调用 t.Failed 方法之后,会获得一个 bool 类型的结果值,它代表了当前的测试函数中的测试是否已被标记为失败。

8. 忽略测试

调用 t.SkipNow 方法目的是标记当前测试函数为已经被忽略,并且立即终止该函数的执行,当前的测试运行程序会转而去执行其他测试函数。与 t.FailNow 方法相同,t.SkipNow 方法也只能在运行测试函数的 Goroutine 中被调用。

调用 t.Skip 方法相当于先后对 t.Logt.SkipNow 方法进行调用,而调用 t.Skipf 方法则相当于先后对 t.Logft.SkipNow 方法进行调用。

方法 t.Skipped 的结果值会告知当前的测试是否已被忽略。

9. 并行运行

方法 t.Parallel 的调用会使当前的测试函数被标记为可并行运行的。这会使测试运行程序可以并发地执行它以及其他可并行运行的测试函数。

10. 功能测试的运行

使用 goc2p 项目的代码包 cnet/ctcppkgtool 为例,如下是下载地址:

https://github.com/hyper-carrot/go_command_tutorial

该代码包中仅包含一个名为 tcp_test.go 的测试源码文件。该测试源码文件包含了两个测试函数。一个是名为 TestPrimeFuncs 的功能测试函数,一个是名为 BenchmarkPrimeFuncs 的基准测试函数。

使用 go test 命令运行 cnet/ctcp 包中的测试结果如下截图:

这里写图片描述

如果只想运行代码包中部分测试的话,有两种方式可以选择:

  • 第一种是 go test 命令后面以测试源码文件及其测试的源码文件为参数,而不是代码包。例如:

    go test envir_test.go envir.go
    
  • 第二种是使用标记 -run-run 标记的值应该为一个正则表达式。名称与此正则表达式匹配的功能测试函数,才会在当次的测试运行过程中被执行。运行截图如下:
    这里写图片描述
    该代码包的测试源码文件 tcp_test.go 中的功能测试函数 TestPrimeFuncs 会被执行。但当正则表达式改为 Prima 后,由于没有 cnet/ctcp 包并没有名称与之匹配的功能测试函数。运行截图如下:
    这里写图片描述

在Go语言中,可以通过方法 t.Logt.Logf 来记录测试过程。但是,在默认情况下,使用此方法打印的信息不会被显示出来的。因此,需要标记 -v , -v 作用是在测试运行结束后打印出所有在测试过程中被记录的日志。出于测试的考虑,强烈建议在测试源码文件中使用方法参数 t 的值上的方法来记录日志。

再看如下的一条示例,同时测试代码包 cnet/ctcp 和代码包 pkgtool,如下运行截图:

这里写图片描述

11. 关于测试运行的时间

现在考虑这样一种测试场景,在一个测试函数包含一段了耗时较长的代码,并且需要严格规定执行这个测试函数的耗时上限。可以在执行 go test 命令时加入标记 -timeout,且在达到其值所代表的时间上限时测试还未结束,那么就会引发一个运行时恐慌。-timeout 标记的值是类型 time.Duration 可以接受的时间表示法。例如,1h20s 代表 1小时20秒2h45m 代表 2小时45分钟200ms 代表 200毫秒

有效的时间单位

时间单位 字符串表示法
纳秒 “ns”
微秒 “us”或“µs”
毫秒 “ms”
“s”
分钟 “m”
小时 “h”

之前运行代码包 cnet/ctcp 中的功能测试函数的执行耗时大约2秒左右。现在通过 -timeout 标记将测试耗时上限设置为100毫秒,并运行测试。如下:

E:\Software\Go\goc2p\src>go test -timeout 100ms cnet/ctcp
panic: test timed out after 100ms
……
FAIL    cnet/ctcp       0.715s

如果只是想让测试尽快结束,使用 -short 标记意味着之后要运行的测试尽量缩短它们的运行时间。代码包 testing 中有一个名为 Short 的函数。这个函数在被调用后会返回一个类型 bool 的值。这个值表明了是否在执行 go test 命令的时候加入了 -short 标记。如果这个函数返回的 bool 值为 true ,那么就可以根据具体情况,去剪裁测试代码从而缩短测试运行时间了。可以在一个功能测试函数中写一段类似的代码:

if testing.Short() {
	multiSend(serverAddr, "SenderT", 1, (2 * time.Second), showLog)
} else {
	multiSend(serverAddr, "SenderT1", 2, (2 * time.Second), showLog)
	multiSend(serverAddr, "SenderT2", 1, (2 * time.Second), showLog)
}

这段代码来自测试源码文件 tcp_test.go 中的测试函数 TestPrimeFuncs,但做了修改,关注点放在了函数 multiSend 上,根据 testing.Short() 的返回的结果值做了不同的策略。

12. 测试的并发执行

如果功能测试运行在拥有多核CPU或者多CPU的计算机上,那么可以使用并发的方式来执行测试。通过 -parallel 标记,能够设置允许并发执行的功能测试函数的最大数量。但能够成为被并发执行的功能测试函数需要具备一个先决条件:在功能测试函数的开始处加入代码 t.Parallel() 。在调用 t.Parallel 方法的时候,执行功能测试函数的测试运行程序会阻塞在这里,并等待其他同样满足并发执行条件的测试函数。当所有需要并行执行的测试函数都被清点且阻塞后,命令程序会根据 -parallel 标记的值,全部或者部分地并发执行这些功能测试函数中的在语句 t.Parallel() 之后的那些代码。

-parallel 标记的默认值是通过标准库的代码包 runtime 的函数 GOMAXPROCS 设置的值。该函数的作用是设置Go语言并发处理的最大数量。实际上,即使 -parallel 标记的值大于这个Go语言最大并发处理数,真正能够并发执行的功能测试函数的数量也不会比它多,所以在通常情况下,并不需要在命令中加入 -parallel 标记,让它的实际值为默认值就好了。但需要注意的是,Go语言最大并发处理数的默认值为 1 。如果想要某些测试函数中的代码被并发地执行,要做的就是在测试源码文件的 init 函数中设置适当的Go语言最大并发处理数,并在这些测试函数中加入语句 t.Parallel()

结语

本篇讲解了Go语言程序测试的 功能测试,下篇讲解Go语言程序测试的 基准测试,尽情期待。

最后附上国内的Go语言社区

Golangtc.com: 该社区是众多的Go语言中文社区中比较活跃的一个。我们可以从中获知很多Go语言方面的信息。网址:http://www.golangtc.com

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go语言学习17-功能测试 的相关文章

随机推荐

  • Idea中快速找到springboot项目和springcloud项目的启动引导类

    一 打开idea下方的service 可以通过alt 8快速打开service 二 单击Add servrice 三 单击Run Configuration Type 四 找到springboot 五 springboot下面就会显示所有的
  • Android(Java)开发之获取BLE广播包(扫描后获取:广播数据+扫描应答数据+RSSI)

    一 安卓BLE的广播包数据从哪获取 通常 安卓APP读写BLE设备的数据都是建立连接后通过GATT获取或修改 但是 BLE设备向外广播时本身会携带一部分有用信息 如将传感数据存放到广播包的自定义数据段 最近接触的一个iBeacon Eddy
  • nginx部署dist包

    第一步 下载nginx压缩包 1 nginx官网下载 gt 点我下载nginx 2 使用wget命令下载 wget c https nginx org download nginx 1 20 1 tar gz 注意 这一步最好在自己的目标目
  • unity DOtween制作连续动画和部分小坑

    一 过程简介 这里首先你需要会普通的用代码做Dotween的动画 这里我们只需要学习如何组合起来 原理 DOtween里面带了一个功能叫做队列 可以连续做动画 需要写代码 基本过程 1 它和平时声明其他东西一样 别人是Int i 这个是Se
  • Oracle表空间及日志查询

    1 查看表空间的名称及大小 SELECT t tablespace name round SUM bytes 1024 1024 0 ts size FROM dba tablespaces t dba data files d WHERE
  • 逆序对计数问题 (python实现)

    本题见于算法导论第三版习题2 4 题设 对于一个序列a1 a2 a3 an 若存在i j 使得i
  • SpringBoot+Redis分布式锁:模拟抢单场景

    本篇不涉及到的redis环境搭建 快速搭建个人测试环境 这里建议使用docker 本篇内容节点如下 1 jedis的nx生成锁 2 如何删除锁 3 模拟抢单动作 10w个人开抢 jedis的nx生成锁 对于java中想操作redis 好的方
  • python实现并行的方法

    在 Python 中 可以使用多种方法来编写并行程序 使用 Python 的多线程模块 threading 可以创建多个线程来并行执行任务 例如 import threading def my function arg Do somethi
  • JS改变input的value值不触发onchange事件解决方案(超简版)

    监听js改变input的值触发的onchange事件 inpstart attr value inpend 0 value 当 我们像上面这样给一个input赋值时 由于onchange时间对input框不起作用 大家首先会想到使用 oni
  • RabbitMQ之延迟队列

    1 概念 延时队列 队列内部是有序的 最重要的特性体现在它的延时属性上 延时队列中的元素是希望再指定时间到了以后或之前取出和处理 简单来说 延时队列就是用来存放需要在指定时间被处理的元素的队列 2 使用场景 订单在十分钟之内未支付则自动取消
  • 显卡天梯图2021年9月新版

    显卡是现在电脑用户非常看重的一个特点 一款显卡的好坏对游戏体验的好坏有着决定性的作用 今天就给你们带来了显卡天梯图最新高清完整版20219月的分享 那么接下来就跟着小编一起来看看显卡天梯图吧 显卡天梯图2021年9月份 显卡又称显示卡 Vi
  • flask迁移数据库时关于枚举字段使用中文报错

    模型类 class Goods CRUDMixin BaseModel db Model tablename goods spu id db Column db Integer primary key True type db Column
  • Java016——Java输入输出语句

    一 输出语句 Java常用的输出语句有三种 1 System out println 换行输出 输出后会自动换行 示例 System out println Hello System out println World 输出 Hello W
  • 填充数据合并单元格_【Excel】为合并单元格填充序号

    戳蓝字 Office随身学 关注我们哦 在表格中填充序号 我们一般使用拖动填充柄的方式 有时为了表格的简洁美观 在表格中经常要对单元格进行合并 可是如果要在合并的单元格中还使用这种方法的话 序号就会出现问题 Excel 会提示你所有合并单元
  • python新手入门代码-介绍十个Python小案例,新手入门就在这里

    案例一 排列组合 要求 将4个数字可能组成的所有互不相同且无重复数字的排列组合列出 注意 很多人学Python过程中会遇到各种烦恼问题 没有人帮答疑容易放弃 为此小编建了个Python全栈免费答疑 裙 七衣衣九起起巴而五 数字的谐音 转换下
  • SDIO DRIVER

    SDIO 卡 SDIO 卡是在 SD 内存卡接口的基础上发展起来的接口 SDIO 接口兼容以前的 SD 内存卡 并且可以连接 SDIO 接口的设备 目前根据 SDIO 协议的 SPEC SDIO 接口支持的设备总类有蓝牙 网卡 电视卡等 S
  • angular 动态组件

    angular动态组件 官方文档有详细介绍 这里仅标记几点以备后记 1 宿主指令 用于动态组件的显示 指令的核心是注入的ViewContainerRef 接口
  • C语言笔记

    1 C语言变量定义 标识符 只能由字母 数字 下划线组成 且首字符不能为数字 int a1 正确 int a2 正确 int 3a 这样定义是错误的 2 整型数据类型 整型数据类型 基本型 int 短整型 short int short 长
  • AttributeError: 'str' object has no attribute 'decode'

    1 背景 keras load model出现如题错误 解决办法 定位到错误处 去掉 decode 思路 根据问题提示 属性错误 str 对象没有属性 decode python3 5和Python2 7在套接字返回值解码上的区别 pyth
  • Go语言学习17-功能测试

    功能测试 引言 功能测试 1 编写功能测试函数 2 常规记录 3 错误记录 4 致命错误记录 5 失败标记 6 立即失败标记 7 失败判断 8 忽略测试 9 并行运行 10 功能测试的运行 11 关于测试运行的时间 12 测试的并发执行 结