Go Convey测试框架入门(go convey gomonkey)

server/2024/9/20 7:25:07/ 标签: golang, 开发语言, 后端, 测试框架, Convey, gomonkey

Convey_0">Go Convey测试框架入门

介绍

GoConvey是一款针对Golang的测试框架,可以管理和运行测试用例,同时提供了丰富的断言函数,并支持很多 Web 界面特性。
Golang虽然自带了单元测试功能,并且在GoConvey框架诞生之前也出现了许多第三方测试框架,但没有一个测试框架像GoConvey一样能够让程序员如此简洁优雅的编写测试代码。

官网:http://smartystreets.github.io/goconvey/

安装

go get 方式安装(go path时代,关闭go mod)

//下载源码并执行go build
//源码下载到$GOPATH/src目录
//go build之后的结果到$GOPATH/bin
go get github.com/smartystreets/goconvey

在$GOPATH/src目录下新增了github.com子目录,该子目录里包含了GoConvey框架的库代码 在$GOPATH/bin目录下新增了GoConvey框架的可执行程序goconvey

go install方式(开启go mod)

在gomod时代一般不需要先显式安装(gomod机制会自动从goproxy拉取依赖到本地cache),只需要go.mod中引入,然后go mod tidy即可。除非要使用GoConvey的web界面,这时需要提前安装GoConvey的二进制,命令为go install github.com/smartystreets/goconvey@latest。

go.mod:

module ziyi.convey.comgo 1.19require github.com/smartystreets/goconvey v1.8.1require (github.com/gopherjs/gopherjs v1.17.2 // indirectgithub.com/jtolds/gls v4.20.0+incompatible // indirectgithub.com/smarty/assertions v1.15.0 // indirect
)
//我后面要演示web页面,因此这里显示安装一下
//安装之后,依然会将编译好的二进制放在$GOPATH/bin
go install github.com/smartystreets/goconvey@latest

我的GOPATH为E:\Go\GoPro:
在这里插入图片描述

使用

注意事项

  1. import goconvey包时,前面加点号".",以减少冗余的代码。凡是在测试代码中看到Convey和So两个方法,肯定是convey包的。我们需要注意不要在产品代码中定义相同的函数名
  2. 测试函数的名字必须以Test开头,而且参数类型必须为*testing.T
  3. 每个测试用例必须使用Convey函数包裹起来,它的第一个参数为string类型的测试描述,第二个参数为测试函数的入参(类型为*testing.T),第三个参数为不接收任何参数也不返回任何值的函数(习惯使用闭包)
  4. Convey函数的第三个参数闭包的实现中通过So函数完成断言判断,它的第一个参数为实际值,第二个参数为断言函数变量,第三个参数或者没有(当第二个参数为类ShouldBeTrue形式的函数变量)或者有(当第二个函数为类ShouldEqual形式的函数变量)

Convey_53">案例一:1个测试用例1个Convey

c_test.go:

package mainimport (. "github.com/smartystreets/goconvey/convey""testing"
)func TestEqualWithSingleTestCase(t *testing.T) {// test name:用例名称// t:需要传入*testing.T// func(){} 测试函数Convey("test name", t, func() {//1+1:断言//ShouldEqual:convey内置的断言//2:期望结果So(1+1, ShouldEqual, 2)})
}

在这里插入图片描述

Convey_77">案例二:多个Convey多个测试用例

①平铺写法

func TestEqualWithMultipleTestCase(t *testing.T) {Convey("test add case", t, func() {So(1+1, ShouldEqual, 2)})Convey("test sub case", t, func() {So(1-1, ShouldEqual, 0)})Convey("test multi case", t, func() {So(1*1, ShouldNotEqual, -1)})
}

Convey_94">②Convey嵌套写法

只需要最外层的Convey传t *testing.T即可

func TestEqualWithMultipleTestCaseAndNested(t *testing.T) {Convey("test case", t, func() {Convey("test add case", func() {So(1+1, ShouldEqual, 2)})Convey("test sub case", func() {So(1-1, ShouldEqual, 0)})Convey("test multi case", func() {So(1*1, ShouldNotEqual, -1)})})
}

案例三:"函数式"断言(assert传入函数)

// 案例三:函数式断言
func TestFunctionalAssertion(t *testing.T) {Convey("test case", t, func() {So(add(1, 1), ShouldEqual, 2)})
}func add(a, b int) int {return a + b
}

Convey_129">案例四:忽略部分Convey断言

针对想忽略但又不想删掉或注释掉某些断言操作,GoConvey提供了Convey/So的Skip方法:

  • SkipConvey函数表明相应的闭包函数将不被执行
  • SkipSo函数表明相应的断言将不被执行

当存在SkipConvey或SkipSo时,测试日志中会显式打上"skipped"形式的标记:

当测试代码中存在SkipConvey时,相应闭包函数中不管是否为SkipSo,都将被忽略。
不管存在SkipConvey还是SkipSo时,测试日志中都有字符串"{n} total assertions (one or more sections skipped)",其中{n}表示测试中实际已运行的断言语句数

// 案例四:忽略Convey断言
//忽略所有断言
func TestCaseSkipConvey(t *testing.T) {SkipConvey("test case", t, func() {So(add(1, 1), ShouldEqual, 2)})
}//忽略某些断言(SkipSo的断言将被忽略)
func TestCaseSkipSo(t *testing.T) {Convey("test case", t, func() {SkipSo(add(1, 1), ShouldEqual, 2)So(1-1, ShouldEqual, 0)})
}

比如我执行TestCaseSkipSo函数:
在这里插入图片描述

案例五:定制断言函数

①原理分析

  1. 查看So源码
func So(actual interface{}, assert Assertion, expected ...any)

在这里插入图片描述
2. 点击查看Assertion结构体

type Assertion func(actual any, expected ...any) string

当Assertion的变量的返回值为""时表示断言成功,否则表示失败:

const assertionSuccess = ""

在这里插入图片描述
3. 结论
因此我们只需要按照结构,实现func,最后返回空串代表断言成功,否则失败

②实际操作

从上面的分析我们可以知道,当Assertion最后返回""代表断言成功,反之失败

// 案例五:定制断言
func TestCustomAssertion(t *testing.T) {Convey("test custom assert", t, func() {So(1+1, CustomAssertionWithRaiseMoney, 2)})
}func CustomAssertionWithRaiseMoney(actual any, expected ...any) string {if actual == expected[0] {return ""} else {return "doesn't raise money"}
}

案例六:访问web页面

如果要访问web页面,需要有goconvey.exe程序并运行,同时需要将xxx_test.go文件放在与exe同目录,否则无法识别到

  • go get “github.com/smartystreets/goconvey”
  • go install “github.com/smartystreets/goconvey” (如果开启了go mod,则用该方式)

默认安装到$GOPATH/bin目录下

执行exe后,访问本地localhost:8080端口即可看到web页面,页面展示的了单测的通过情况等。
在这里插入图片描述

gomonkey_209">拓展:gomonkey(打桩工具)

如果在被测函数中调用了其他函数(比如其他业务方的),可以使用以下方法,gomonkey打桩工具

  • 官网:https://github.com/agiledragon/gomonkey
//安装依赖
go get "github.com/agiledragon/gomonkey"

给全局变量打桩

使用 gomonkey 可以方便地模拟函数的行为


// 拓展:配合monkey打桩
var num = 10 //全局变量func TestApplyGlobalVar(t *testing.T) {Convey("TestApplyGlobalVar", t, func() {Convey("change", func() {//模拟函数行为,给全局变量复制,在函数结束后直接通过reset恢复全局变量值patches := gomonkey.ApplyGlobalVar(&num, 150)defer patches.Reset()So(num, ShouldEqual, 150)})Convey("recover", func() {So(num, ShouldEqual, 10)})})
}

给函数打桩

//对函数进行打桩
func TestFunc(t *testing.T) {// mock 了 networkCompute(),返回了计算结果2patches := gomonkey.ApplyFunc(networkCompute, func(a, b int) (int, error) {return 2, nil})defer patches.Reset()sum, err := Compute(1, 2)println("expected %v, got %v", 2, sum)if sum != 2 || err != nil {t.Errorf("expected %v, got %v", 2, sum)}
}func networkCompute(a, b int) (int, error) {// do something in remote computerc := a + breturn c, nil
}func Compute(a, b int) (int, error) {sum, err := networkCompute(a, b)return sum, err
}

全部代码:

欢迎大家star在这里插入图片描述

  • Github:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-convey
    在这里插入图片描述

c_test.go:

package mainimport ("github.com/agiledragon/gomonkey"_ "github.com/agiledragon/gomonkey". "github.com/smartystreets/goconvey/convey""testing"
)// 案例一:一个Convey,一个用例
func TestEqualWithSingleTestCase(t *testing.T) {// test name:用例名称// t:需要传入*testing.T// func(){} 测试函数Convey("test name", t, func() {//1+1:断言//ShouldEqual:convey内置的断言//2:期望结果So(1+1, ShouldEqual, 2)})
}// 案例二:多个Convey,多个用例(平铺写法)
func TestEqualWithMultipleTestCase(t *testing.T) {Convey("test add case", t, func() {So(1+1, ShouldEqual, 2)})Convey("test sub case", t, func() {So(1-1, ShouldEqual, 0)})Convey("test multi case", t, func() {So(1*1, ShouldNotEqual, -1)})
}// 案例二:多个Convey,多个用例(嵌套写法)
func TestEqualWithMultipleTestCaseAndNested(t *testing.T) {Convey("test case", t, func() {Convey("test add case", func() {So(1+1, ShouldEqual, 2)})Convey("test sub case", func() {So(1-1, ShouldEqual, 0)})Convey("test multi case", func() {So(1*1, ShouldNotEqual, -1)})})
}// 案例三:函数式断言
func TestFunctionalAssertion(t *testing.T) {Convey("test case", t, func() {So(add(1, 1), ShouldEqual, 2)})
}func add(a, b int) int {return a + b
}// 案例四:忽略Convey断言
// 忽略所有断言
func TestCaseSkipConvey(t *testing.T) {SkipConvey("test case", t, func() {So(add(1, 1), ShouldEqual, 2)})
}// 忽略某些断言(SkipSo的断言将被忽略)
func TestCaseSkipSo(t *testing.T) {Convey("test case", t, func() {SkipSo(add(1, 1), ShouldEqual, 2)So(1-1, ShouldEqual, 0)})
}// 案例五:定制断言
func TestCustomAssertion(t *testing.T) {Convey("test custom assert", t, func() {So(1+1, CustomAssertionWithRaiseMoney, 2)})
}func CustomAssertionWithRaiseMoney(actual any, expected ...any) string {if actual == expected[0] {return ""} else {return "doesn't raise money"}
}// 拓展:配合monkey打桩
var num = 10 //全局变量func TestApplyGlobalVar(t *testing.T) {Convey("TestApplyGlobalVar", t, func() {Convey("change", func() {//模拟函数行为,给全局变量复制,在函数结束后直接通过reset恢复全局变量值patches := gomonkey.ApplyGlobalVar(&num, 150)defer patches.Reset()So(num, ShouldEqual, 150)})Convey("recover", func() {So(num, ShouldEqual, 10)})})
}// 对函数进行打桩
func TestFunc(t *testing.T) {// mock 了 networkCompute(),返回了计算结果2patches := gomonkey.ApplyFunc(networkCompute, func(a, b int) (int, error) {return 2, nil})defer patches.Reset()sum, err := Compute(1, 2)println("expected %v, got %v", 2, sum)if sum != 2 || err != nil {t.Errorf("expected %v, got %v", 2, sum)}
}func networkCompute(a, b int) (int, error) {// do something in remote computerc := a + breturn c, nil
}func Compute(a, b int) (int, error) {sum, err := networkCompute(a, b)return sum, err
}

go.mod:

module ziyi.convey.comgo 1.19require (github.com/agiledragon/gomonkey v2.0.2+incompatiblegithub.com/smartystreets/goconvey v1.8.1
)require (github.com/gopherjs/gopherjs v1.17.2 // indirectgithub.com/jtolds/gls v4.20.0+incompatible // indirectgithub.com/smarty/assertions v1.15.0 // indirect
)

参考文章:
http://smartystreets.github.io/goconvey/
https://www.jianshu.com/p/e3b2b1194830


http://www.ppmy.cn/server/104955.html

相关文章

第132天:内网安全-横向移动Exchange服务有账户CVE漏洞无账户口令爆破

域控环境0day.org 通过网盘分享的文件:131-0day.org内网域环境镜像文件 链接: https://pan.baidu.com/s/1rf_gHVJSNG8PEsiSr7DFSw?pwdr5jc 提取码: r5jc 给win7设置一张nat网卡,其他各个主机都设置为vm2 案例一: 域横向移动-内网服务-Exchan…

[kaggle竞赛] 毒蘑菇的二元预测

毒蘑菇的二元预测 您提供了很多关于不同二元分类任务的资源和链接,看起来这些都是Kaggle竞赛中的参考资料和高分解决方案。为了帮助您更好地利用这些资源,这里是一些关键点的总结: Playground Season 4 Episode 8 主要关注的竞赛: 使用银行…

Java ArrayList和LinkedList

ArrayList ArrayList是Java中最常用的数据结构之一,它是一个动态数组的实现,允许你在程序中存储和管理一个可变大小的对象列表,我们可以添加或删除元素。 ArrayList 继承了 AbstractList ,并实现了 List 接口。 基本概念 Arra…

Git(面试篇)

目录 配置操作 全局配置 当前仓库配置 查看global配置 查看当前仓库配置 删除global配置 删除当前仓库配置 本地操作 查看变更情况 将当前目录及其子目录下所有变更都加入到暂存区 将仓库内所有变更都加入到暂存区 将指定文件添加到暂存区 比较工作区和暂存区的所有…

JavaScript学习文档(5):为什么需要函数、函数使用、函数传参、函数返回值、作用域、匿名函数、逻辑中断

目录 一、为什么需要函数 1、函数 2、说明 二、函数使用 1、函数的声明语法 2、函数名命名规范 3、函数调用语法 4、函数体 5、函数案例(数字求和) (1)计算1-100之间所有数字的和 三、函数传参 1、声明语法 2、调用语…

【Redis】Redis数据结构——List列表

List列表 命令lpushlpushxrpushrpushxlrangelpoprpoplindexlinsertllen 阻塞版本命令blpopbrpop 命令⼩结内部编码使用场景消息队列分频道的消息队列微博 Timeline 列表类型是⽤来存储多个有序的字符串,如图 2-19 所⽰,a、b、c、d、e 五个元素从左到右组…

Java 使用线程池和CountDownLatch分批插入或者更新数据

需求:在开发业务报表时,需要从MySQL数据库读取数据后进行操作,然后写入数据库,使用定时任务跑批。 分析:①兼顾性能,② MySQL没有Oracle那么方便、强大的存储过程。综上所述,使用线程池以分批提…

python dash框架 油气田可视化软件设计文档

V1.1:机器学习框架(神经网络) 时间范围优化 表格布局优化 添加前端设计元素布局 V1.0:基础布局和对应计算函数 要求 首先第一部分是通过神经网络预测天然气流量,其中输入开始时间和截止时间是为了显示这一段时间内的天然气流量预测结果 第二部分&…

前端实现首次访问,后续从本地访问

在前端实现将PDF文件下载到用户的本地磁盘&#xff0c;并在后续加载时使用本地文件&#xff0c;而不是重新从服务器下载&#xff0c;可以通过以下步骤实现&#xff1a; 1. **使用<a>标签的download属性**&#xff1a;当用户首次点击下载PDF时&#xff0c;通过<a>标…

私有仓库tomcat镜像构建

通过Tomcat安装包构建镜像 Dockerfile # 使用官方的OpenJDK镜像作为基础镜像 FROM xa-test.harbor.com:55555/idaas/openjdk:8u232 ENV CATALINA_HOME/usr/local/tomcat ENV PATH$CATALINA_HOME/bin:$PATH # 将Tomcat的压缩包复制到镜像中并解压到指定目录 COPY apache-tomcat…

Apollo9.0 PNC源码学习之Planning模块—— Lattice规划(六):横纵向运动轨迹评估

参考文章: (1)Apollo6.0代码Lattice算法详解——Part6:轨迹评估及碰撞检测对象构建 (2)自动驾驶规划理论与实践Lattice算法详解 0 前言 横纵向运动轨迹的评估,主要通过构建定点巡航和定点停车两个场景下,对纵向运动参考速度、加速度、加加速度的大小进行检验和过滤,然…

1.初识redis

文章目录 1.认识redis1.1 mysql和redis 对比1.2分布式系统1.2.1单机架构与分布式架构1.2.2数据库分离(应用服务器和存储服务器分离)与负载均衡1.2.3负载均衡器1.2.4 数据库读写分离1.2.5 数据库服务器引入缓存1.2.6数据库分库分表1.2.7 引入微服务 2.常见概念解释2.1 应用(Appl…

JDK15.0.2安装

JDK15.0.2安装 1. 下载 下载地址&#xff1a; https://www.oracle.com/java/technologies/downloads/archive/ 通过百度网盘分享的文件&#xff1a;jdk-15.0.2_windows-x64_bin.exe 链接&#xff1a;https://pan.baidu.com/s/15AOcTby3YLSp26_btCkEIw 提取码&#xff1a;vs7…

10. 指针数组和数组指针详细区别

指针数组和数组指针在存储位置和占用内存大小方面也有显著的区别&#xff0c;尤其是它们的结构不同导致内存分布上的差异。接下来详细说明它们在这两个方面的区别&#xff1a; 1. 指针数组 (Array of Pointers) 定义回顾&#xff1a; int *array[5];这里 array 是一个指针数…

K8S部署MySQL5.7的主从服务

mysql-slave-0是master mysql-slave-1是备份 当mysql写的时候&#xff0c;找headless service中的 mysql-slave-0.mysql57-slave-headless&#xff1b;当mysql读的时候&#xff0c;找clusterip service中的mysql57-slave-read读&#xff0c;实现读写分离。 statefulset维护两个…

Spring + Boot + Cloud + JDK8 + Elasticsearch 单节点 模式下实现全文检索高亮-分页显示 快速入门案例

1. 安装elasticsearchik分词器插件 sudo wget https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-8.13.4.zip sudo mkdir -p ./es_plugins/analysis-ik sudo mkdir ./es_data sudo unzip elasticsearch-analysis-ik-8.13.4.zip -d ./es_plugins/a…

探索提示工程 Prompt Engineering的奥妙

一、探索提示工程 1. 介绍通用人工智能和专用人工智能 人工智能&#xff08;AI&#xff09;可以分为通用人工智能&#xff08;AGI&#xff09;和专用人工智能&#xff08;Narrow AI&#xff09;。AGI是一种能够理解、学习和执行任何人类可以完成的任务的智能。与此相对&#x…

Neo4j 图数据库入门

图形数据库存储节点和关系&#xff0c;而不是表或文档。数据的存储方式就像你在白板上勾画想法一样。您的数据存储不受预定义模型的限制&#xff0c;允许以非常灵活的方式考虑和使用它。 一、核心概念&#xff1a;属性图形模型 Neo4j使用属性图数据库模型。图数据结构由节点(离…

【图形学】TA之路-基于Unity Shader编程之初体验

学习shader之前你必须知道的事情&#xff1a; Unity开发引擎、Direct3D、Shader他们之间的关系 Direct3D 是一个底层图形 API&#xff0c;它直接与 GPU &#xff08;显卡&#xff09;交互&#xff0c;提供了访问硬件加速功能的接口。Unity 开发引擎&#xff0c;它封装了很多底…

自然语言处理系列三十九》条件随机场CRF算法原理

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》&#xff08;人工智能科学与技术丛书&#xff09;【陈敬雷编著】【清华大学出版社】 文章目录 自然语言处理系列三十九条件随机场(CRF)算法原理CRF与HMM 总结 …