Spock单元测试框架使用介绍和实践

背景

单元测试是保证我们写的代码是我们想要的结果的最有效的办法。根据下面的数据图统计,单元测试从长期来看也有很大的收益。
bug在单元测试阶段发小修复耗时最少
85%的缺陷都在代码设计阶段产生,而发现bug的阶段越靠后,耗费成本就越高,指数级别的增高

单元测试收益:

  • 它是最容易保证代码覆盖率达到100%的测试。
  • 可以⼤幅降低上线时的紧张指数。
  • 单元测试能更快地发现问题。
  • 单元测试的性价比最高,因为错误发现的越晚,修复它的成本就越高,而且难度呈指数式增长,所以我们要尽早地进行测试。
  • 编码人员,一般也是单元测试的主要执行者,是唯一能够做到生产出无缺陷程序的人,其他任何人都无法做到这一点。
  • 有助于源码的优化,使之更加规范,快速反馈,可以放心进行重构。
    我们都知道单元测试的好处,但是我们遇到的项目还是很多都缺乏单测,一方面是因为单测耗时,一方面也是因为业务的复杂性导致单测难以进行,最长见还是任务重、工期紧,或者干脆就不写了。

为什么要用Spock,和JUnit有什么区别

Spock是一款国外优秀的测试框架,基于BDD(行为驱动开发)思想实现,功能非常强大。Spock结合Groovy动态语言的特点,提供了各种标签,并采用简单、通用、结构化的描述语言,让编写测试代码更加简洁、高效。官方介绍:https://spockframework.org/
在这里插入图片描述

Spock的主要特点

  • 让测试代码更规范,内置多种标签来规范单元测试代码的语义,测试代码结构清晰,更具可读性,降低后期维护难度。
  • 提供多种标签,比如:given、when、then、expect、where、with、thrown……帮助我们应对复杂的测试场景。
  • 使用Groovy这种动态语言来编写测试代码,可以让我们编写的测试代码更简洁,适合敏捷开发,提高编写单元测试代码的效率。
  • 遵从BDD(行为驱动开发)模式,有助于提升代码的质量。
  • IDE兼容性好,自带Mock功能。
    现有的单测框架比如junit、jmock、mockito都是相对独立的工具,针对不同的业务场景提供特定的解决方案。在业务场景中有很多第三方服务的依赖,比如微服务的调用、数据库、redis等的存储,我们需要将这些依赖mock掉去验证我们代码的逻辑是否正确。JMock或Mockito虽然提供了mock功能,可以把接口等依赖屏蔽掉,但不提供对静态类静态方法的mock,PowerMock或Jmockit虽然提供静态类和方法的mock,但它们之间需要整合(junit+mockito+powermock),语法繁琐,而且这些工具并没有告诉你**“单元测试代码到底应该怎么写?”**

Spock通过提供规范描述,定义多种标签(given、when、then、where等)去描述代码“应该做什么”,输入条件是什么,输出是否符合预期,从语义层面规范代码的编写。

Spock使用简单介绍

基本概念

Spock定义了几个构造块,一段单测也是由这几个构造块组合完成的。
Spock主要提供了如下基本构造块:

  • given: mock单测中指定mock数据
  • when: 触发行为,比如调用指定方法或函数
  • then: 做出断言表达式
  • expect: 期望的行为,when-then的精简版
  • where: 以表格的形式提供测试数据集合
  • thrown: 如果在when方法中抛出了异常,则在这个子句中会捕获到异常并返回
  • def setup() {} :每个测试运行前的启动方法
  • def cleanup() {} : 每个测试运行后的清理方法
  • def setupSpec() {} : 第一个测试运行前的启动方法
  • def cleanupSpec() {} : 最后一个测试运行后的清理方法

通过这些标签从行为上规范单测代码,每一种标签对应一种语义,让我们的单测代码结构具有层次感,功能模块划分清晰,便于后期维护
IntelliJ IDEA支持format(opt+cmd+L)格式化快捷键,因为表格列的长度不一样,手动对齐比较麻烦。

实际案例

/*** 身份证号码工具类<p>* 15位:6位地址码+6位出生年月日(900101代表1990年1月1日出生)+3位顺序码* 18位:6位地址码+8位出生年月日(19900101代表1990年1月1日出生)+3位顺序码+1位校验码* 顺序码奇数分给男性,偶数分给女性。* @author 公众号:Java老K* 个人博客:www.javakk.com*/
public class IDNumberUtils {/*** 通过身份证号码获取出生日期、性别、年龄* @param certificateNo* @return 返回的出生日期格式:1990-01-01   性别格式:F-女,M-男*/public static Map<String, String> getBirAgeSex(String certificateNo) {String birthday = "";String age = "";String sex = "";int year = Calendar.getInstance().get(Calendar.YEAR);char[] number = certificateNo.toCharArray();boolean flag = true;if (number.length == 15) {for (int x = 0; x < number.length; x++) {if (!flag) return new HashMap<>();flag = Character.isDigit(number[x]);}} else if (number.length == 18) {for (int x = 0; x < number.length - 1; x++) {if (!flag) return new HashMap<>();flag = Character.isDigit(number[x]);}}if (flag && certificateNo.length() == 15) {birthday = "19" + certificateNo.substring(6, 8) + "-"+ certificateNo.substring(8, 10) + "-"+ certificateNo.substring(10, 12);sex = Integer.parseInt(certificateNo.substring(certificateNo.length() - 3,certificateNo.length())) % 2 == 0 ? "女" : "男";age = (year - Integer.parseInt("19" + certificateNo.substring(6, 8))) + "";} else if (flag && certificateNo.length() == 18) {birthday = certificateNo.substring(6, 10) + "-"+ certificateNo.substring(10, 12) + "-"+ certificateNo.substring(12, 14);sex = Integer.parseInt(certificateNo.substring(certificateNo.length() - 4,certificateNo.length() - 1)) % 2 == 0 ? "女" : "男";age = (year - Integer.parseInt(certificateNo.substring(6, 10))) + "";}Map<String, String> map = new HashMap<>();map.put("birthday", birthday);map.put("age", age);map.put("sex", sex);return map;}
}

在这里插入图片描述

下面案例是测试断言的比较器的单测
在这里插入图片描述

运行结果:
在这里插入图片描述

代码简洁清晰
在这里插入图片描述

在这里插入图片描述

安装环境配置&第三方依赖

添加依赖

<!-- spock -->
<dependency><groupId>org.spockframework</groupId><artifactId>spock-core</artifactId><version>2.4-M1-groovy-4.0</version><scope>test</scope>
</dependency>
<dependency><groupId>org.spockframework</groupId><artifactId>spock-spring</artifactId><version>2.4-M1-groovy-4.0</version><scope>test</scope>
</dependency>

If Esle 多分支场景测试

使用expect+where
expect相当于when+then的精简版
@Unroll注解表示展开where标签下面的每一行测试,作为单独的case跑
[图片]

异常测试

使用thrown
有些方法会根据不同的场景抛出不同的异常,Spock内置thrown()方法,可以捕获调用业务代码抛出的预期异常并验证,再结合where表格的功能,可以很方便的覆盖多种自定义业务异常,代码如下:
在这里插入图片描述

Void方法测试

没有返回值的方法该怎么测试呢,一种有效的测试方式,就是验证方法内部逻辑和流程是否符合预期,比如:

  • 应该走到哪个分支逻辑?
  • 是否执行了这一行代码?
  • for循环中的代码执行了几次?
  • 变量在方法内部的变化情况?
    在这里插入图片描述
    在这里插入图片描述

静态方法测试

https://javakk.com/302.html

动态Mock

具体场景介绍

使用Spock简化测试代码

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

基本构造块####

Spock主要提供了如下基本构造块:

  • where: 以表格的形式提供测试数据集合
  • when: 触发行为,比如调用指定方法或函数
  • then: 做出断言表达式
  • expect: 期望的行为,when-then的精简版
  • given: mock单测中指定mock数据
  • thrown: 如果在when方法中抛出了异常,则在这个子句中会捕获到异常并返回
  • def setup() {} :每个测试运行前的启动方法
  • def cleanup() {} : 每个测试运行后的清理方法
  • def setupSpec() {} : 第一个测试运行前的启动方法
  • def cleanupSpec() {} : 最后一个测试运行后的清理方法
    了解基本构造块的用途后,可以组合它们来编写单测。

参考资料

美团 https://tech.meituan.com/2021/08/06/spock-practice-in-meituan.html
老K的Java博客:https://javakk.com/category/spock
Spring mock: https://spockframework.org/spock/docs/2.2-SNAPSHOT/module_spring.html

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

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

相关文章

20.x86游戏实战-远线程注入的实现

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

Ubuntu 24.04安装Jellyfin媒体服务器图解教程

使用 Jellyfin 等开源软件创建媒体服务器肯定能帮助您管理和跨各种设备传输媒体集合。当你有一个封闭社区时&#xff0c;这尤其有用。 什么是 Jellyfin 媒体服务器&#xff1f; Jellyfin 媒体服务器&#xff0c;顾名思义&#xff0c;是一款开源软件&#xff0c;允许用户使用本…

vue使用audio 音频实现播放与关闭(可用于收到消息给提示音效)

这次项目中因为对接了即时通讯 IM&#xff0c;有个需求就是收到消息需要有个提示音效&#xff0c;所以这里就想到了用HTML5 提供的Audio 标签&#xff0c;用起来也是很方便&#xff0c;首先让产品给你个提示音效&#xff0c;然后你放在项目中&#xff0c;使用Audio 标签&#x…

GESP CCF C++ 三级认证真题 2024年6月

第 1 题 小杨父母带他到某培训机构给他报名参加CCF组织的GESP认证考试的第1级&#xff0c;那他可以选择的认证语言有&#xff08;&#xff09;种。 A. 1 B. 2 C. 3 D. 4 第 2 题 下面流程图在yr输入2024时&#xff0c;可以判定yr代表闰年&#xff0c;并输出 2月是29天 &#x…

百日筑基第二十三天-23种设计模式-创建型总汇

百日筑基第二十三天-23种设计模式-创建型总汇 前言 设计模式可以说是对于七大设计原则的实现。 总体来说设计模式分为三大类&#xff1a; 创建型模式&#xff0c;共五种&#xff1a;单例模式、简单工厂模式、抽象工厂模式、建造者模式、原型模式。结构型模式&#xff0c;共…

Hadoop3:HDFS存储优化之小文件归档

一、情景说明 我们知道&#xff0c;NameNode存储一个文件元数据&#xff0c;默认是150byte大小的内存空间。 那么&#xff0c;如果出现很多的小文件&#xff0c;就会导致NameNode的内存占用。 但注意&#xff0c;存储小文件所需要的磁盘容量和数据块的大小无关。 例如&#x…

SpringBoot整合阿里云RocketMQ对接,商业版

1.需要阿里云开通商业版RocketMQ 普通消息新建普通主题,普通组,延迟消息新建延迟消息主题,延迟消息组 2.结构目录 3.引入依赖 <!--阿里云RocketMq整合--><dependency><groupId>com.aliyun.openservices</groupId><artifactId>ons-client</…

Unity3d开发google chrome的dinosaur游戏

游戏效果 游戏中&#xff1a; 游戏中止&#xff1a; 一、制作参考 如何制作游戏&#xff1f;【15分钟】教会你制作Unity小恐龙游戏&#xff01;新手15分钟内马上学会&#xff01;_ unity教学 _ 制作游戏 _ 游戏开发_哔哩哔哩_bilibili 二、图片资源 https://download.csdn.…

MySQL 事务与锁

事务ACID特性 原子性&#xff1a;事务要么同时成功&#xff0c;要么同时失败&#xff0c;事务的原子性通过undo log日志保证 一致性&#xff1a;业务代码要抛出报错&#xff0c;让数据库回滚 隔离性&#xff1a;事务并发执行时&#xff0c;他们内部操作不能互相干扰 持久性&…

数据分析01——系统认识数据分析

1.数据分析的全貌 1.1观测 1.1.1 观察 &#xff08;1&#xff09;采集数据 a.采集数据&#xff1a;解析系统日志 当你在看视频的时候———就会产生日志———解析日志———得到数据 b.采集数据&#xff1a;埋点获取新数据&#xff08;自定义记录新的信息&#xff09; 日志…

JavaScript学习笔记(九)

56、JavaScript 类 56.1 JavaScript 类的语法 请使用关键字 class 创建一个类。 请始终添加一个名为 constructor() 的方法。 JavaScript 类不是对象。 它是 JavaScript 对象的模板。 语法&#xff1a; class ClassName {constructor() { ... } }示例&#xff1a;例子创…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(六)-人工智能控制的自主无人机用例

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

MongoDB教程(六):mongoDB复制副本集

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、MongoD…

在 vite+vue3+electron 中使用 express

文章目录 一、Vite Vue3 Electron 项目的搭建二、搭建 express 环境1、安装 express 框架所需依赖2、创建 express 项目3、配置路由4、启动 express 服务5、启动 electron 并获取数据 三、项目打包 一、Vite Vue3 Electron 项目的搭建 详细的项目构建和打包可参考另一篇文…

c语言 Program to print pyramid pattern (打印金字塔图案的程序)

编写程序打印由星星组成的金字塔图案 例子 &#xff1a; 输入&#xff1a;n 6输出&#xff1a; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 我们强烈建…

MATLAB科研数据可视化教程

原文链接&#xff1a;MATLAB科研数据可视化https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247609462&idx3&snf7043936fc5ee42b833c7c9f3bcd24ba&chksmfa826d91cdf5e4872eb275e5319b66ba6927ea0074fb2293fe1ca47d6aedf38ab91050be484c&token1551213…

k8s集群 安装配置 Prometheus+grafana

k8s集群 安装配置 Prometheusgrafana k8s环境如下&#xff1a;机器规划&#xff1a; node-exporter组件安装和配置安装node-exporter通过node-exporter采集数据显示192.168.40.180主机cpu的使用情况显示192.168.40.180主机负载使用情况 Prometheus server安装和配置创建sa账号&…

Redis中数据分片与分片策略

概述 数据分片是一种将数据分割并存储在多个节点上的技术&#xff0c;可以有效提高系统的扩展性和性能。在Redis中&#xff0c;数据分片主要用于解决单个实例存储容量和性能瓶颈的问题。通过将数据分散存储到多个Redis节点中&#xff0c;可以将负载均衡到不同的服务器上&#…

ArcGIS Enterprise 命令行组件创建配置

1. 创建ArcGIS Server站点 使用 createsite工具 命令行直接执行 createsite.sh [-u <arg>] [-p <arg>] [-d <arg>] [-c <arg>]执行文件 createsite.sh [-f <FILE>]安装目录下会有类似的创建站点文件&#xff1a; 修改其中的内容&#xff0c;…

芯片基础 | Verilog结构级描述和操作符(上)

术语定义(Terms and Definitions) 结构描述(Structural Modeling) 用门及门的连接描述器件的功能基本单元(primitives原语) Verilog语言已定义的具有简单逻辑功能的功能模型(models)结构描述 结构描述等价于逻辑图,它们都是连接简单元件来构成更为复杂的元件;Verilog使用其连接…