goland单元测试

news2024/11/22 10:44:53

一、单元测试的概念


1.1 什么是单元测试,有什么用?

单元测试是针对于函数的测试,用来保证该函数的逻辑正确性。

1.2 单元测试的要求?


1. 单元测试在正式上线之前应该全部自动执行,并且需要保证全部通过
2. 单元测试需要构建 【输入】 和 【预期输出】的case,case需要靠人工构建,涵盖各种边界情况
3. 单元测试需要测试到代码的每一个逻辑分支
4. 单元测试关注的是代码逻辑是否正确,无需关注网络调用、数据查询等,对于此部分代码可以mock掉
5. 单元测试行覆盖率要达到70%,函数覆盖率要达到100%(controller层除外)

1.3 单元测试的常见误区


1. 单元测试行覆盖率100%只能保证写的测试用例走过了所有的代码,但不能保证代码逻辑完全无误

1.4 gomock工具-gomonkey


官方文档 :https://github.com/agiledragon/gomonkey
使用教程:https://cloud.tencent.com/developer/article/1872029
https://www.ddhigh.com/2021/09/18/gomonkey-private-method-stub/ 解决了无法打桩私有方法的问题

二、单元测试实战

func GetFinalStatus(productName string) (status string, r ocommon.ResultInfo) {
	daoLockPtr := dao.CreateProductLockInfoPtr()
	query := oquery.NewQueryStructOfTable()
	var allLockList []dao.ProductLockInfo
	query.AddConditonsByOperator("ProductName", oquery.OP_EQUAL, productName)
	r = daoLockPtr.SearchByQuery(&allLockList, query)
	if !r.IsOk() {
		return
	}
	// 如果产品线没有查到锁记录,则改产品线锁状态为【未锁定】
	if len(allLockList) == 0 {
		status = LOCK_STATUS_UNLOCK
		return
	}
	// 遍历所有的锁信息
	for _, lockItem := range allLockList {
		// 一旦发现硬锁定,则立即返回产品线锁状态为【硬锁定】
		if lockItem.LockStatus == LOCK_STATUS_HARDLOCK {
			status = LOCK_STATUS_HARDLOCK
			return
		}
	}
	// 遍历完也不存在硬锁定,那就一定是【软锁定】
	status = LOCK_STATUS_SOFTLOCK
	return
}
快速生成单元测试代码

编写测试用例
参考1.2 中的第三点,我们需要涵盖到每一个分支,所以该单元测试至少要有下面三个测试用例

单元测试用例用例一:测试软锁定用例二:测试硬锁定用例三:测试未锁定
输入SIODSIODSIOD
输出软锁定        硬锁定未锁定

mock数据并完成测试代码
参数1.2 中的第四点,该方法的单元测试关注的是代码逻辑是否正确,对于数据的来源不关注,再加上1.2 中的第一点,单元测试需要在每次提交代码之前,全部执行通过
所以我们需要mock掉 daoLockPtr.SearchByQuery 方法,并且将allLockList 变量用mock数据替代
如果不mock掉daoLockPtr.SearchByQuery,而是每次都去从数据库中查询,不可能同时通过上述三个测试用例
此时就用到了上面提到的测试工具,对daoLockPtr.SearchByQuery(&allLockList, query) 方法进行打桩 并mock掉 allLockList数据

具体实现如下方代码
func TestGetFinalStatus(t *testing.T) {
	type args struct {
		productName string
	}
	tests := []struct {
		name       string
		args       args
		wantStatus string
		wantR      ocommon.ResultInfo
		mockData   []dao.LockInfo // 此变量存储每一个测试用例的mock数据
	}{
		// TODO: Add test cases.
		{
			name:       "未锁定",
			args:       args{productName: "SIOD"},
			wantStatus: "unLock",
			wantR:      ocommon.ResultInfo{ErrNo: 0},
			mockData: nil, // 对于未锁定,mockData就是nil
		},
		{
			name:       "软锁定",
			args:       args{productName: "SIOD"},
			wantStatus: "softLock",
			wantR:      ocommon.ResultInfo{ErrNo: 0},
			mockData:   []dao.LockInfo{dao.LockInfo{LockStatus: LOCK_STATUS_SOFTLOCK}},// 对于软锁定,mockData的一种情况就是一条锁的状态为 【软锁】
		},
		{
			name:       "硬锁定",
			args:       args{productName: "SIOD"},
			wantStatus: "hardLock",
			wantR:      ocommon.ResultInfo{ErrNo: 0},
			mockData:   []dao.LockInfo{dao.LockInfo{LockStatus: LOCK_STATUS_HARDLOCK}},// 对于硬锁定,mockData的一种情况就是一条锁的状态为 【硬锁】
		},
	}
	for _, tt := range tests {
		// 使用 gomonkey 来 mock 方法
		patches := gomonkey.ApplyMethod(reflect.TypeOf(&dbBase.DbBase{}), "SearchByQuery", func(_ *dbBase.DbBase, allLockList interface{}, query *oquery.QueryStructOfTable) ocommon.ResultInfo {
			if data, ok := allLockList.(*[]dao.LockInfo); ok {
				*data = tt.mockData
			}
			return ocommon.ResultInfo{}
		})
		t.Run(tt.name, func(t *testing.T) {
			gotStatus, gotR := GetFinalStatus(tt.args.productName)
			if gotStatus != tt.wantStatus {
				t.Errorf("GetFinalStatus() gotStatus = %v, want %v", gotStatus, tt.wantStatus)
			}
			if !reflect.DeepEqual(gotR.ErrNo, tt.wantR.ErrNo) {
				t.Errorf("GetFinalStatus() gotR = %v, want %v", gotR.ErrNo, tt.wantR.ErrNo)
			}
		})
		// 每一个测试用例结束后,将mock的数据清除,不影响下一次mock
		patches.Reset()
	}
}

查看单元测试覆盖率
我们在该方法所在的目录下面执行下面的命令,然后将单元测试的信息输出到目录下的c.out
执行单元测试

GOOS=darwin GOARCH=amd64 go test -cover -coverprofile=c.out -gcflags="all=-N -l" -run 'TestGetFinalStatus'


查看单元测试覆盖率,执行完之后,会自动打开一个html网页

go tool cover -html=c.out


从下图中,可以看出,我们在lock_info.go文件的单元测试覆盖率只有10.3%,我们需要把该文件中的其他方法根据项目实际情况补充单元测试


该方法中只有一行代码没有覆盖到,就是数据库查询错误,单元测试的目的是确保后续的代码逻辑正确,所以这一行代码可以不做测试

如何写出更加完备的单元测试?
上面代码可以看到对于该方法的覆盖已经达到每一个分支(数据库错误处理分支除外),但是其并不能保证其代码逻辑一定没有问题。
所以我们需要再补充几个单元测试用例,正如1.2 中的第二条一样,单元测试的难点就是需要人工去构思各种边界情况

{
	name:    "混合锁取最高级别",
	args:    args{productName: "SIOD"},
	wantStatus: "hardLock",
	wantR:   ocommon.ResultInfo{ErrNo: 0},
	mockData:  []dao.ProductLockInfo{{LockStatus: LOCK_STATUS_HARDLOCK}, {LockStatus: LOCK_STATUS_SOFTLOCK}}, // 此处的mockData 是一个硬锁,一个软锁
},
{
	name:    "多把相同状态的锁",
	args:    args{productName: "SIOD"},
	wantStatus: "hardLock",
	wantR:   ocommon.ResultInfo{ErrNo: 0},
	mockData:  []dao.ProductLockInfo{{LockStatus: LOCK_STATUS_HARDLOCK}, {LockStatus: LOCK_STATUS_HARDLOCK}}, // 此处的mockData 是一个硬锁,一个软锁
},


参考文章
https://www.liwenzhou.com/posts/Go/unit-test-0/#c-0-1-8
https://time.geekbang.org/column/article/10275?utm_campaign=geektime_search&utm_content=geektime_search&utm_medium=geektime_search&utm_source=geektime_search&utm_term=geektime_search

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

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

相关文章

ArcGIS定义投影与投影的区别(数据和底图不套合的原因和解决办法)

今天介绍一下ArcGIS中定义投影与投影的区别。 给大家解惑一下为什么经常出现自己的数据无法和底图套合的情况。 一 目录 1、ArcGIS定义投影与投影的概念区别 2、ArcGIS定义正确的坐标系 3、ArcGIS动态投影实现套合 4、ArcGIS地理坐标系转投影坐标系(错误做法&am…

语义通信论文略读(十七)可变长编码的语义通信+Extended Reality

Semantic Communications With Variable-Length Coding for Extended Reality 用于扩展现实的带可变长度编码的语义通信 作者: Bowen Zhang, Zhijin Qin, Geoffrey Ye Li 所属机构: 伦敦帝国理工学院电气与电子工程系; 清华大学电子工程系 关键词: 扩展现实,联…

IDEA2019搭建Springboot项目基于java1.8 解决Spring Initializr无法创建jdk1.8项目 注释乱码

后端界面搭建 将 https://start.spring.io/ 替换https://start.aliyun.com/ 报错 打开设置 修改如下在这里插入代码片 按此方法无果 翻阅治疗后得知 IDEA2019无法按照网上教程修改此问题因此更新最新idea2024或利用插件Alibaba Clouod Toolkit 换用IDEA2024创建项目 下一步…

网络编程 day1.2~day2——TCP和UDP的通信基础(TCP)

笔记脑图 作业&#xff1a; 1、将虚拟机调整到桥接模式联网。 2、TCP客户端服务器实现一遍。 服务器 #include <stdio.h> #include <string.h> #include <myhead.h> #define IP "192.168.60.44" #define PORT 6666 #define BACKLOG 20 int mai…

BLIP-2模型的详解与思考

大模型学习笔记------BLIP-2模型的详解与思考 1、BLIP-2框架概述2、BLIP-2网络结构详解3、BLIP-2的几点思考 上一篇文章上文中讲解了 BLIP&#xff08;Bootstrapping Language-Image Pretraining&#xff09;模型的一些思考&#xff0c;本文将讲述一个BLIP的升级版 BLIP-2&am…

strcat的模拟实现(递归)(c基础)

hi &#xff0c; I am 36 适合对象c语言初学者 strcat(arr1,arr2);函数是将arr2接到arr1后&#xff0c;返回arr1 链接介绍一下strcat(c基础)-CSDN博客 下面通过自定义函数来模拟实现 缺陷 该函数不适合arr1[] "";时因为该函数的功能是把arr2接到arr1后&#xf…

GeekChallenge 2024 第十五届极客大挑战 pwn AK

GeekChallenge 2024 第十五届极客大挑战 pwn AK &#x1f340;前言☘️ez_shellcode&#xff08;shellcode&#xff0c;栈溢出&#xff09;&#x1f33f;分析&#x1f33f;解题&#x1f33f;exp ☘️买黑吗喽了吗&#xff08;整数溢出&#xff0c;栈溢出&#xff09;&#x1f3…

springboot源码02-springboot启动主要步骤总结

文章目录 【README】【1】springboot应用启动主要步骤【1.1】springboot应用启动主要步骤总结 【2】springboot应用启动先后发布事件【2.1】springboot启动先后发布事件代码实践【2.1.1】自定义监听器【2.1.2】自定义SpringApplicationRunListener【2.1.3】springboot运行日志 …

数据结构——小小二叉树第二幕(二叉树链式结构的实现以及二叉树的遍历)超详细!!!

文章目录 前言一、实现链式结构二叉树1.1 前中后序遍历1.1.1 遍历规则 1.2 二叉树的结点个数以及高度等 总结 前言 上一篇我们初步认识了树的结构以及概念&#xff0c;同时也学习到了二叉树的顺序结构&#xff08;堆&#xff09;的实现&#xff0c;以及堆的初步应用。 时隔几日…

服务器端渲染 (SSR) 与客户端渲染 (CSR)

嘿程序员&#xff01;我们都知道&#xff0c;新时代的 Javascript 已经彻底改变了现代网站的结构和用户体验。如今&#xff0c;网站的构建更像是一个应用程序&#xff0c;伪装成一个能够发送电子邮件、通知、聊天、购物、支付等的网站。今天的网站是如此先进、互动&#xff0c;…

【spring】spring单例模式与锁对象作用域的分析

前言&#xff1a;spring默认是单例模式&#xff0c;这句话大家应该都不陌生&#xff1b;因为绝大多数都是使用单例模式&#xff0c;避免了某些问题&#xff0c;可能导致对某些场景缺乏思考。本文通过结合lock锁将单例模式、静态变量、锁对象等知识点串联起来。 文章目录 synchr…

解析与修复vcruntime140_1.dll问题,总结四种vcruntime140_1.dll解决方法

在使用Windows系统的过程中&#xff0c;不少用户可能会遇到与vcruntime140_1.dll相关的问题。这个看似神秘的文件&#xff0c;其实在很多软件的运行中扮演着至关重要的角色。今天的这篇文章将教大家四种vcruntime140_1.dll解决方法。 一、vcruntime140_1.dll文件分析 &#xf…

django基于django的民族服饰数据分析系统的设计与实现

摘 要 随着网络科技的发展&#xff0c;利用大数据分析对民族服饰进行管理已势在必行&#xff1b;该平台将帮助企业更好地理解服饰市场的趋势&#xff0c;优化服装款式&#xff0c;提高服装的质量。 本文讲述了基于python语言开发&#xff0c;后台数据库选择MySQL进行数据的存储…

如何使用GPT API 自定义 自己的 RAG

要使用 GPT 的 API 实现自己的 RAG&#xff08;Retrieval-Augmented Generation&#xff09; 系统&#xff0c;可以结合检索工具和 GPT 模型&#xff0c;将外部知识库中的信息与生成模型结合起来&#xff0c;完成准确、高效的任务。以下是具体步骤和实现方法&#xff1a; 系统架…

对subprocess启动的子进程使用VSCode python debugger

文章目录 1 情况概要&#xff08;和文件结构&#xff09;2 具体设置和启动步骤2.1 具体配置Step 1 针对attach debugger到子进程Step 2 针对子进程的暂停(可选) Step 3 判断哪个进程id是需要的子进程 2.2 启动步骤和过程 3 其他问题解决3.13.2 ptrace: Operation not permitted…

cocos creator 3.8 一些简单的操作技巧,材质的创建 1

这是一个飞机的3D模型与贴图 导入到cocos中&#xff0c;法线模型文件中已经包含了mesh、material、prefab&#xff0c;也就是模型、材质与预制。界面上创建一个空节点Plane&#xff0c;将模型直接拖入到Plane下。新建材质如图下 Effect属性选择builtin-unlit&#xff0c;不需…

基于web的音乐网站(Java+SpringBoot+Mysql)

目录 1系统概述 1.1 研究背景 1.2研究目的 1.3系统设计思想 2相关技术 2.1 MYSQL数据库 2.2 B/S结构 2.3 Spring Boot框架简介 3系统分析 3.1可行性分析 3.1.1技术可行性 3.1.2经济可行性 3.1.3操作可行性 3.2系统性能分析 3.2.1 系统安全性 3.2.2 数据完整性 …

Web中间件漏洞总结——IIS篇

0x01 前言 渗透过程中会遇到各种中间件&#xff0c;某些中间件版本存在远程执行、任意文件上传等漏洞。本文对IIS相关漏洞进行整理&#xff0c;方便我们在渗透过程中快速查阅IIS漏洞。文章粗略浅显&#xff0c;适合刚入行的新手观看。 0x02 目录 IIS6.0 PUT漏洞IIS6.0 远程代…

关于中断向量表中没有EXTIx_IRQHandler的问题

如果你在中断向量表查找中断向量服务函数时&#xff0c;没有查找到EXTI7_IRQHandler等&#xff0c;是因为中断向量中根本就没有这个函数。 STM32 的中断向量表通常由启动文件&#xff08;如 startup_stm32f1xx.s&#xff09;定义。在该文件中&#xff0c;所有的中断服务例程&a…

idea启动服务报错Application run failed

现象是这样&#xff0c;在宝兰德部署报错&#xff1a; NoClassDefFoundError: org/apache/tomcat/util/codec/binary/Base64 本地启动报错&#xff1a;Application run failed:Failed to parse configuration class [***.WebApplication]; nested exception is java.lang.Illeg…