优测云测试平台 | 有效的单元测试(下)
接着上一篇内容,我们继续~
四、测试的目标之三:快速反馈
测试的快速反馈有两个方面的含义:
-
1.测试运行要快速出结果。
-
2.当测试失败时,要能快速定位失败原因。
测试运行效率决定了开发的工作周期运转的快慢。在理想的 TDD 模型中,开发人员一遍又一遍地重复着“测试 -> 实现 -> 测试“ 这样的周期循环,直到所有用例通过。持续集成和持续交付的过程也是如此。不管是单元测试还是大型测试,运行效率都是应该追求的目标。
同样在的道理,当测试出现用例失败时,如果我们要花很长的时间来定位到原因,也会拖慢我们从“测试”到“实现”的速度。要提高快速定位的能力,一方面要提高被测代码的可观测性,另一方面要给用例合理命名,还有在断言时加入有价值,易读易懂的 message. 在去年的一次分享中,已经提到过如何建设被测系统的可观测性并且做了一些支撑工具。在用例命名和断言信息方面,越是大型测试要求越高,因为其所覆盖的代码范围广,失败节点多,而在单测中则相对要求低一些,因为其被测代码覆盖范围小,相对容易定位。
但是!在单元测试时依然要认真给用例命名,充分添加断言信息。这一建议单独另起一段,考虑的是当我们写单测时的心智问题。不少开发在写单测时多心智是:“单测是写给我自己看的,这个用例测试的是我负责的代码,出了问题我很快就知道定位”,但是从团队和业务的角度出发,测试都是写给整个开发团队看的,这与代码的 readability 是一样的。code for team, test for team! 测试用例怎样命名,断言信息写些啥,有很多博客和问答可以提供参考.这里提供一个建议:在命名和添加断言信息时,想象着它们在报告中是如何显示的。以下用一个实际的例子来对比一下断言信息好坏的明显区别。
用例 v1
func TestQueryRecentExecs(t *testing.T) {t.Run("count 100 is too large", func(t *testing.T) {proxy := clientProxy()ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)defer cancelFunc()req := &caselog.QueryCaseRecentExecsRequest{CaseId: 5,Count: 100,}_, err := proxy.QueryCaseRecentExecs(ctx, req)require.Error(t, err)assert.Equal(t, errs.ErrorTypeBusiness, trpcErrType(err))})
}
该用例中,被测接口要求入参中
Count
不能大于等于 100,我们向被测服务发起异常参数的请求,期望其返回业务类型的错误(不能返回框架类型的错误,框架类型的错误是诸如服务寻址失败,超时之类的,跟业务无关的错误,具体参考 trpc 错误手册)。某次运行用例失败后得到的 log 如下:
test log v1
Failed
=== RUN TestQueryRecentExecs/count_100_is_too_largecaselog_test.go:56: Error Trace: caselog_test.go:56Error: Not equal: expected: 2actual : 1Test: TestQueryRecentExecs/count_100_is_too_large--- FAIL: TestQueryRecentExecs/count_100_is_too_large (2.00s)
从这个错误信息中,我们从用例名中知道目的是验证
Count
值为 100 过大而产生错误,但是在错误信息中我们读到的是“因为我们期望 2 而实际值为 1,用例失败”。这里 2 和 1 分别是什么?没有信息。
下面进行一版改进:
用例 v2
func TestQueryRecentExecs(t *testing.T) {t.Run("large count 100 cause biz error", func(t *testing.T) {proxy := clientProxy()ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)defer cancelFunc()req := &caselog.QueryCaseRecentExecsRequest{CaseId: 5,Count: 100,}_, err := proxy.QueryCaseRecentExecs(ctx, req)require.Error(t, err)assert.Equal(t, "business", trpcErrTypeName(err), "unexpected trpc error type")})
}
test log v2
Failed
=== RUN TestQueryRecentExecs/large_count_100_cause_biz_errorcaselog_test.go:80: Error Trace: caselog_test.go:80Error: Not equal: expected: "business"actual : "framework"Diff:--- Expected+++ Actual@@ -1 +1 @@-business+frameworkTest: TestQueryRecentExecs/large_count_100_cause_biz_errorMessages: unexpected trpc error type--- FAIL: TestQueryRecentExecs/large_count_100_cause_biz_error (0.81s)
v2 中很明确,trpc error type 与预期不符,预期是 business 而实际值为 framework. 这是一个接口测试,出现 Framework error 通常是被测服务没有正确部署或者测试流水线所在网络环境与被测服务不通。(用例中使用 testify 断言,其断言格式固定,可能不一定是最好的格式,略显啰嗦,但是它在其他方面比较方便,为了整个项目统一我们只能“因地制宜”。更简洁的断言信息是
t.Errorf("QueryCaseRecentExecs response error type mismatch got = %s, want = %s", trpcErrTypeName(err), "business")
即使该用例不是我写的,只要具备基本的 trpc 背景知识,看到报告后第一反应就是去确认被测服务是否健康,以及流水线网络环境是否正确,而在 v1 中,我可能还得打开 ide 查看一下用例代码,看用例代码还只能知道不是 business 错误,实际是什么错误并不知道,还得跳转到 trpc 的源代码才知道 1 是 framework 错误。(还有另一种错误是 “callee framework”)。孰快孰慢一目了然。
五、测试的第四个目标:用例集的可维护性
-
1.功能代码有可读性要求,测试代码也有,同样,功能代码有可维护性要求,测试代码也有可维护性要求。可维护性最佳实践与功能代码是相通的,仅举几例:
DRY, 以提取函数/提取常量来替代复制粘贴。 -
2.以配置代替写死值,可以参考一些 go 的标准库里面单元测试的方法,可以在测试时指定 flag,在用例中 parseFlags 来读取配置。
-
3.不要滥用设计模式,测试代码复杂度不宜过高,否则我们是否还有给测试代码写测试代码?
参考阅读
《Unit Testing: Principles, Practices, and Patterns》
脚注
[1]覆盖率并不一定是越高越好,对于没有测试价值的,低风险的代码,强行覆盖会消耗过多的时间和精力。怎样定一个覆盖率红线是另一个话题,长话短说就是代码库的 owner 自行决定才是最科学的。
[2]那些单元测试已经充分测试过的逻辑,无需在接口测试和端到端测试中重复。
优测测试平台简介:
是一个为企业与开发者提供专业的测试工具和服务的平台,沉淀十年产品测试经验,提供终端测试、接口测试、性能测试、安全测试等多领域测试服务与产品,协助客户提高效率降低成本,保证产品质量。
相关文章:
优测云测试平台 | 有效的单元测试(下)
接着上一篇内容,我们继续~ 四、测试的目标之三:快速反馈 测试的快速反馈有两个方面的含义: 1.测试运行要快速出结果。 2.当测试失败时,要能快速定位失败原因。 测试运行效率决定了开发的工作周期运转的快慢。在理想的 TDD 模型中&#x…...
CUDA安装
在cmd中输入nvidia-smi。显示CUDA Version:12.3,所以只能下载小于等于12.3的版本。如下图: 进这个网址:https://developer.nvidia.com/cuda-toolkit-archive 选择一个版本下载。 选择完后之后这样选择: 最后点击下载即…...
【XTDrone Ubuntu18.04】XTDrone + Ubuntu18.04 + PX4 安装过程
重新配置所有的软件 卸载之前安装的ROS GAZEBO 记得把/home下的.ros和.gazebo也删除,删就删干净 参考链接:ROS的卸载与安装 血泪总结!亲测有效 卸载ROS方法 正式安装 安装依赖 sudo apt install ninja-build exiftool ninja-build protobuf…...
网站使用什么协议比较好
网站协议大多数使用HTTP和HTTPS HTTP协议,超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议。 HTTP是应用层协议,同其他应用层协议一样,是为了实现某一类具体应用的协议&…...
18. 机器学习——集成学习
机器学习面试题汇总与解析——集成学习 本章讲解知识点 什么是集成学习AdaBoost梯度提升树(Gradient Boosting Decision Tree, GBDT)随机森林(Random Forest,简称RF)XGBoostLightGBM本专栏适合于Python已经入门的学生或人士,有一定的编程基础。 本专栏适合于算法工程师、机器…...
SimaPro生命周期评估建模与碳足迹分析流程
SimaPro以系统和透明的方式轻松建模和分析复杂的生命周期,通过确定供应链中每个环节的热点,从原材料的提取到制造,分销,使用和处置,衡量所有生命周期阶段的产品和服务对环境的影响。SimaPro是过去25年评估生命周期的最…...
我的项目分享(不喜勿喷)
我要分享的项目是大喇叭C2C电商平台系统,一个面向移动端的电子商务平台,为个体消费者和商家提供直接交易和沟通的便利,丰富了人们的生活。 主要功能模块: 该项目的主要功能包括: 1. 用户注册功能:使用正则…...
PyTorch:张量与矩阵
PyTorch 是一个基于 Python 的科学计算包,专门针对深度学习研究,提供了丰富的工具和库。在 PyTorch 中,张量(tensor)是深度学习的核心数据结构,它可以看作是可以进行自动微分的多维数组。张量不仅可以代表标…...
传统广电媒体为何选择上云?有何优势?
随着现在互联网和科技的发展,现在更多的行业都搭上了科技这辆快车快速的完成了转型,那么在传统的广电媒资行业他们目前有哪些痛点呢?传统广电媒体转型发展现状是什么?企业如何数字化转型?企业上云的优势有哪些…...
系列十、堆参数调优
一、堆内存调优参数 -Xms堆空间的最小值,默认为物理内存的1/64-Xmx堆空间的最大值,默认为物理内存的1/4-XX:PrintGCDetails输出详细的GC处理日志 二、获取堆内存的默认物理内存 /*** Author : 一叶浮萍归大海* Date: 2023/11/16 14:50* Description: 获…...
sqlite3简单使用
为什么要使用sqlite3? sqlite3轻量简介,无需单独的数据库服务,只需访问磁盘上的.db的文件。在某些情况下很有用。 下面是一些简单的使用代码: import sqlite3 from uuid import uuid1# 连接数据库文件,如果不存在会创建 with…...
实测文心一言4.0,真的比GPT-4毫不逊色吗?
10月17日,李彦宏在百度世界2023上表示。当天,李彦宏以《手把手教你做AI原生应用》为主题发表演讲,发布文心大模型4.0版本。 今天,咱们就开门见山啊。这一回要测一测,昨天才发布的文心一言大模型 4.0。 之所以要测它&…...
损失函数——KL散度(Kullback-Leibler Divergence,KL Divergence)
KL散度(Kullback-Leibler Divergence,简称KL散度)是一种度量两个概率分布之间差异的指标,也被称为相对熵(Relative Entropy)。KL散度被广泛应用于信息论、统计学、机器学习和数据科学等领域。 KL散度衡量的…...
基于springboot的医护人员排班系统 全套代码 全套文档
基于springboot的医护人员排班系统,springboot vue mysql (毕业论文10411字以上,共27页,程序代码,MySQL数据库) 代码下载链接:https://pan.baidu.com/s/177HdCGtTvqiHP4O7qWAgxA?pwd0jlf 提取码:0jlf 【运行环境】 IDEA, JDK1.8, Mysql, Node, Vue …...
【YOLOX简述】
YOLOX的简述 一、 原因1. 背景2. 概念 二、 算法介绍2.1 YOLOX算法结构图:2.2 算法独特点2.3 Focus网络结构2.4 FPN,PAN2.5 BaseConv2.6 SPP2.7 CSPDarknet2.8 YOlO Head 三、预测曲线3.1 曲线 一、 原因 1. 背景 工业的缺陷检测是计算机视觉中不可缺少…...
一文带你深入浅出Web的自动化测试工具Selenium【建议收藏】
文章目录 前言第01节 Selenium概述第02节 安装浏览器驱动(以Google为例)第03节 定位页面元素1. 打开指定页面2. id 定位3. name 定位4. class 定位5. tag 定位6. xpath 定位7. css 选择器8. link 定位9. 示例 有道翻译 第04节 浏览器控制1. 修改浏览器窗…...
Django模版层
解析: forloop内置对象:运行结果解析 counter0: 从0开始计数 counter : 从1开始计数 first: True,判断循环的开始 last : Tues,判断循环的结束模版变量的书写 我们可以在html中编写python代码。 演示: {{ 填写变量 }}{% 填写类的 %}{{ d.0 }} {{ d.1 }…...
同一个IP地址可有不同的写法?
每个人在上网的时候,都会被分配一个IP地址,这是互联网世界中的“身份证号码”。IP地址是以数字形式呈现的,例如192.168.1.1。然而,你是否知道,尽管一个IP地址的数字串唯一标识一个设备,但它可以有不同的写法…...
《Effective C++》条款13
以对象管理资源 有这样一段代码: class A { public:A* create(){...}}; class B :public A { public:A* ptr create();...delete ptr; }; 我们定义了ptr去接收create()函数的返回值,并且在最后进行了回收资源。看似是没问题的。但是实际上有很多隐患&am…...
【入门Flink】- 09Flink水位线Watermark
在窗口的处理过程中,基于数据的时间戳,自定义一个“逻辑时钟”。这个时钟的时间不会自动流逝;它的时间进展,就是靠着新到数据的时间戳来推动的。 什么是水位线 用来衡量事件时间进展的标记,就被称作“水位线”&#x…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
高分辨率图像合成归一化流扩展
大家读完觉得有帮助记得关注和点赞!!! 1 摘要 我们提出了STARFlow,一种基于归一化流的可扩展生成模型,它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流(TARFlow&am…...
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章 摘要: 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言,受限于 C 语言本身的内存安全和并发安全问题,开发复杂模块极易引入难以…...
Netty自定义协议解析
目录 自定义协议设计 实现消息解码器 实现消息编码器 自定义消息对象 配置ChannelPipeline Netty提供了强大的编解码器抽象基类,这些基类能够帮助开发者快速实现自定义协议的解析。 自定义协议设计 在实现自定义协议解析之前,需要明确协议的具体格式。例如,一个简单的…...
