简述spock以及使用

1. 介绍

1.1 Spock是什么?

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

Spock是一个Java和Groovy`应用的测试和规范框架。之所以能够在众多测试框架中脱颖而出,是因为它优美而富有表现力的规范语言。Spock的灵感来自JUnit、RSpec、jMock、Mockito、Groovy、Scala、Vulcans。

简单来讲,Spock主要特点如下:

  • 让测试代码更规范,内置多种标签来规范单元测试代码的语义,测试代码结构清晰,更具可读性,降低后期维护难度。

  • 提供多种标签,比如:givenwhenthenexpectwherewiththrown……帮助我们应对复杂的测试场景。

  • 使用Groovy这种动态语言来编写测试代码,可以让我们编写的测试代码更简洁,适合敏捷开发,提高编写单元测试代码的效率。

  • 遵从BDD(行为驱动开发)模式,有助于提升代码的质量。

  • IDE兼容性好,自带Mock功能。

Spock和JUnit、jMock、Mockito的区别在哪里?

总的来说,JUnit、jMock、Mockito都是相对独立的工具,只是针对不同的业务场景提供特定的解决方案。其中JUnit单纯用于测试,并不提供Mock功能。

我们的服务大部分是分布式微服务架构。服务与服务之间通常都是通过接口的方式进行交互。即使在同一个服务内也会分为多个模块,业务功能需要依赖下游接口的返回数据,才能继续后面的处理流程。这里的下游不限于接口,还包括中间件数据存储比如Squirrel、DB、MCC配置中心等等,所以如果想要测试自己的代码逻辑,就必须把这些依赖项Mock掉。因为如果下游接口不稳定可能会影响我们代码的测试结果,让下游接口返回指定的结果集(事先准备好的数据),这样才能验证我们的代码是否正确,是否符合逻辑结果的预期。

尽管jMock、Mockito提供了Mock功能,可以把接口等依赖屏蔽掉,但不能对静态方法Mock。虽然PowerMock、jMockit能够提供静态方法的Mock,但它们之间也需要配合(JUnit + Mockito PowerMock)使用,并且语法上比较繁琐。工具多了就会导致不同的人写出的单元测试代码“五花八门”,风格相差较大。

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

Spock自带Mock功能,使用简单方便(也支持扩展其他Mock框架,比如PowerMock),再加上Groovy动态语言的强大语法,能写出简洁高效的测试代码,同时能方便直观地验证业务代码的行为流转,增强工程师对代码执行逻辑的可控性。

1.2 使用Spock解决单元测试开发中的痛点

如果在(if/else)分支很多的复杂场景下,编写单元测试代码的成本会变得非常高,正常的业务代码可能只有几十行,但为了测试这个功能覆盖大部分的分支场景,编写的测试代码可能远不止几十行。

尽管使用JUnit的@Parametered参数化注解或者DataProvider方式可以解决多数据分支问题,但不够直观,而且如果其中某一次分支测试Case出错了,它的报错信息也不够详尽。

这就需要一种编写测试用例高效、可读性强、占用工时少、维护成本低的测试框架。首先不能让业务人员排斥编写单元测试,更不能让工程师觉得写单元测试是在浪费时间。而且使用JUnit做测试工作量不算小。据初步统计,采用JUnit的话,它的测试代码行和业务代码行能到3:1。如果采用Spock作为测试框架的话,它的比例可缩减到1:1,能够大大提高编写测试用例的效率。

举个例子:

public double calc(double income) {BigDecimal tax;BigDecimal salary = BigDecimal.valueOf(income);if (income <= 0) {return 0;}if (income > 0 && income <= 3000) {BigDecimal taxLevel = BigDecimal.valueOf(0.03);tax = salary.multiply(taxLevel);} else if (income > 3000 && income <= 12000) {BigDecimal taxLevel = BigDecimal.valueOf(0.1);BigDecimal base = BigDecimal.valueOf(210);tax = salary.multiply(taxLevel).subtract(base);} else if (income > 12000 && income <= 25000) {BigDecimal taxLevel = BigDecimal.valueOf(0.2);BigDecimal base = BigDecimal.valueOf(1410);tax = salary.multiply(taxLevel).subtract(base);} else if (income > 25000 && income <= 35000) {BigDecimal taxLevel = BigDecimal.valueOf(0.25);BigDecimal base = BigDecimal.valueOf(2660);tax = salary.multiply(taxLevel).subtract(base);} else if (income > 35000 && income <= 55000) {BigDecimal taxLevel = BigDecimal.valueOf(0.3);BigDecimal base = BigDecimal.valueOf(4410);tax = salary.multiply(taxLevel).subtract(base);} else if (income > 55000 && income <= 80000) {BigDecimal taxLevel = BigDecimal.valueOf(0.35);BigDecimal base = BigDecimal.valueOf(7160);tax = salary.multiply(taxLevel).subtract(base);} else {BigDecimal taxLevel = BigDecimal.valueOf(0.45);BigDecimal base = BigDecimal.valueOf(15160);tax = salary.multiply(taxLevel).subtract(base);}return tax.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();}

能够看到上面的代码中有大量的if-else语句,Spock提供了where标签,可以让我们通过表格的方式来测试多种分支。

@Unroll
def "个税计算,收入:#income, 个税:#result"() {expect: "when + then 的组合"CalculateTaxUtils.calc(income) == resultwhere: "表格方式测试不同的分支逻辑"income || result-1     || 00      || 02999   || 89.973000   || 90.03001   || 90.111999  || 989.912000  || 990.012001  || 990.224999  || 3589.825000  || 3590.025001  || 3590.2534999  || 6089.7535000  || 6090.035001  || 6090.354999  || 12089.755000  || 1209055001  || 12090.3579999  || 20839.6580000  || 20840.080001  || 20840.45
}

在这里插入图片描述

由此可见, 使用spock框架比junit更能节省代码, 但是spock是使用的是Groovy语法, 所以需要学习一下它的语法, 因为Groovy是基于java虚拟机的语言, 所以也能直接写java代码(不过java语法繁琐, 用java的话和用junit差不多了)

2. 使用篇

2.1 引入pom

				<!--引入 groovy 依赖--><dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy-all</artifactId><version>2.4.15</version><scope>test</scope></dependency><!--引入spock 与 spring 集成包--><dependency><groupId>org.spockframework</groupId><artifactId>spock-spring</artifactId><version>1.2-groovy-2.4</version><scope>test</scope></dependency>
<!-- Spock自带Mock功能,所以我们可以来Mock非静态方法。但是遇到静态方法时,我们需要导入powermock --><!--powermock --><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito2</artifactId><version>2.0.0</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>2.0.0</version><scope>test</scope></dependency>

2.2 使用案例

import org.mockito.InjectMocks
import org.mockito.Matchers
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.slf4j.Logger
import spock.lang.Specification
import spock.lang.Unrollimport java.time.LocalDate
import java.time.LocalDateTime
import java.time.Month
import java.time.temporal.ChronoUnitimport static org.mockito.Mockito.when
class QueryDateTest extends Specification {@Unroll("#testName")def "测试特定时间tess"() {when: "执行以及结果验证"def queryDate = new QueryDate()queryDate.setSpecialDateType(sepecial)// 赋值, << 表示(集合类型)右边赋值给左边   (Groovy语法)     
//	    queryDate.getCustomDateTimes() << [time1, time2]then:def actual = queryDate.getCustomDateTimes()def collect = actual.collect { arr -> "[${arr*.toString().join(',')}]" }.join(',')rStr == collectwhere: "测试用例覆盖"testName                            | sepecial         || rStr"1-分钟粒度-近12小时-最后一天"      | Arrays.asList(5) || "{2024-01-19T05:00,2024-01-19T17:00}""2-10分钟粒度-近12小时-中间天"      | Arrays.asList(4) || "{2024-01-18T00:00,2024-01-19T00:00}""3-10分钟粒度-绝对时间-中间天-跨天" | Arrays.asList(1) || "{2024-01-15T00:00,2024-01-16T00:00}"}
}

结果如下:

在这里插入图片描述

解释:

  1. @Unroll , 可以让where中的每一个案例当成一个单测, 如此可以在输出结果时,分开查看当前用例执行情况 (如果不加, 则合并一起展示)

  2. #testName 是一个变量, 在where中复制, 表示, 当前用例的名称 (参见输出结果)

  3. 测试特定时间tess 表示这个单测方法的名称 (其实java也允许中文函数名称)

  4. when, then, where 是spock的关键字

    1. when: 表示当如何时, 可以做一些赋值,定义之类的工作
    2. then: 表示然后如何, 可以做业务逻辑的操作
    3. where: 表示条件如何, 这里就是写大量条件的地方

    当然, 你也可以把 whenthen合并, 也可以不要 where, 把输入条件全部写在then中 (这就和juint一样了), 后面再介绍一下还有其他关键字和组合用法

  5. defcollect{} 是 Groovy语法

  6. where 后面是一个类似表格的东西, 里面就是入参, 第一行是定义变量名, 这里的变量名可以再whenthen中使用, 多个列使用|单竖线隔开,||双竖线区分输入和输出变量,即左边是输入值,右边是输出值 (其实全部写成 || 也可以)(此处的用法是, 入参和期望结果都是变量形式)

    格式: 输入参数1 | 输入参数2 || 输出结果1 | 输出结果2

2.3 分块(关键字)

分块替换功能说明
givensetup初始化函数、MOCK非必要
whenexpect执行待测试的函数when 和 then 必须成对出现
thenexpect验证函数结果when 和 then 可以被 expect 替换
where多套测试数据的检测spock的特性功能
and对其余块进行分隔说明非必要

Spock单元测试框架介绍以及在美团优选的实践 - 美团技术团队 (meituan.com)

吃透单元测试:Spock单元测试框架的应用与实践-CSDN博客

Spock-高质量单元测试实操篇 - My_blog_s - 博客园 (cnblogs.com)

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

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

相关文章

php配合fiddler批量下载淘宝天猫商品数据分享

有个做电商的朋友问我,每次上款,需要手动去某宝去搬运商品图片视频,问我能不能帮忙写个脚本,朋友开口了,那就尝试一下 首先打开某宝,访问一款商品,找出他的数据来源 通过观察我们发现主图数据来这个接口输出 h5api.m.taobao.com/h5/mtop.taobao.pcdetail.data.get/1.0 …

如何学习创建和使用 Java 归档(JAR)文件

1. 简介 JAR&#xff08;Java ARchive&#xff09;文件是一种用于打包多个Java类、资源文件和元数据的压缩文件格式。它在Java开发和发布过程中扮演着重要角色。通过使用JAR文件&#xff0c;开发者可以将应用程序的所有组件打包在一个文件中&#xff0c;方便分发和部署。 2. …

世界森林覆盖率分布图

原文链接https://mp.weixin.qq.com/s?__bizMzUyNzczMTI4Mg&mid2247680287&idx1&sn6ac57fa7472fc58cad1d5ab11b1d6b3b&chksmfa775e22cd00d7341e4f59d52221fb7f9a8e2d83602ab58719481af66b2f3b153c57f01c68bb&token808263816&langzh_CN&scene21#wec…

开源语音合成模型ChatTTS本地部署结合内网穿透实现远程访问

文章目录 前言1. 下载运行ChatTTS模型2. 安装Cpolar工具3. 实现公网访问4. 配置ChatTTS固定公网地址 前言 本篇文章就教大家如何快速地在Windows中本地部署ChatTTS&#xff0c;并且我们还可以结合Cpolar内网穿透实现公网随时随地访问ChatTTS AI语言模型。 最像人声的AI来了&a…

Airflow任务流调度

0 前言 Airflow是Airbnb内部发起的一个工作流管理平台。使用Python编写实现的任务管理、调度、监控工作流平台。Airflow的调度依赖于crontab命令&#xff0c;与crontab相比&#xff0c;Airflow可以方便地查看任务的执行状况&#xff08;执行是否成功、执行时间、执行依赖等&…

基于OCC+OSG的读取IGS模型显示其装配以及模型颜色

一般来说&#xff0c;读取STP模型会解析其装配结构&#xff0c;而读取IGS模型时候一般不这么做&#xff0c;因为IGS的每个部件大多是面片&#xff0c;而非一个实体模型&#xff0c;所以比如一些开源软件&#xff0c;比如Freecad等都是直接将模型作为一个整体并且在模型树上只显…

Elixir学习笔记——try, catch, and rescue

Elixir 有三种错误机制&#xff1a;errors, throws, and exits。在本章中&#xff0c;我们将探索每种机制&#xff0c;并说明何时应使用它们。 Errors 当代码中发生异常时&#xff0c;就会使用错误&#xff08;或异常&#xff09;。可以通过尝试将数字添加到原子来检索示例错…

生活好物:日常更精彩

我们的日用杂货店&#xff0c;是生活美学的聚集地。这里汇聚了各式各样的生活用品&#xff0c;每一件都蕴含着对生活的热爱与追求。 走进我们的日用杂货店&#xff0c;仿佛打开了一个充满生活气息的宝藏盒。从厨房的锅碗瓢盆&#xff0c;到浴室的洗漱用品&#xff0c;再到客厅的…

6.17 作业

使用qt实现优化自己的登录界面 要求&#xff1a; 1. qss实现 2. 需要有图层的叠加 &#xff08;QFrame&#xff09; 3. 设置纯净窗口后&#xff0c;有关闭等窗口功能。 4. 如果账号密码正确&#xff0c;则实现登录界面关闭&#xff0c;另一个应用界面显示。 第一个源文件 …

9.无代码爬虫软件做网页数据抓取流程——弹出窗口的移除

首先&#xff0c;多数情况下免费版本的功能&#xff0c;已经可以满足绝大多数采集需求&#xff0c;想了解八爪鱼采集器版本区别的详情&#xff0c;请访问这篇帖子&#xff1a; https://blog.csdn.net/cctv1123/article/details/139581468 八爪鱼采集器免费版和个人版、团队版下…

反射复习(java)

文章目录 反射机制的作用反射机制的原理加载机制详细解释 获取 Class 对象反射获取构造方法&#xff1a;获取 Class 对象里面 Constructor 对象反射获取成员变量&#xff1a;获取Class 对象里面的 Field 对象反射获取成员方法&#xff1a;获取 Class 对象里的 Method 对象其他常…

15.编写自动化测试(下)

标题 三、控制测试流程3.1 添加测试参数3.2 并行或连续运行测试3.3 显示函数输出3.4 指定/过滤测试用例名称3.5 忽略某些测试用例3.6 只运行被忽略的测试 四、测试的组织结构4.1 概念引入4.2 测试私有函数4.2 单元测试4.3 集成测试4.4 集成测试中的子模块4.5 二进制crate的集成…

Python脚本中使用 if 语句导致的错误代码

在 Python 脚本中使用 if 语句是一种常见的控制流程结构&#xff0c;用于根据条件决定程序的执行路径。当使用 Python 中的 if 语句时&#xff0c;可能会导致一些常见的错误。下面就是我经常遇到的错误代码示例及其可能的原因和解决方法&#xff0c;希望对大家有些帮助&#xf…

死锁预防之银行家算法

死锁预防之银行家算法 第一章 概述 Dijkstra提出了一种能够避免死锁的调度算法,称为银行家算法。 它的模型基于一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,每个客户都有一个贷款额度,银行家知道不可能所有客户同时都需要最大贷款额,所以他只保留一定单位…

韩国职场新趋势:员工拒绝晋升,追求工作与生活的平衡

在当前职场环境中&#xff0c;晋升通常被视为职业生涯发展的重要里程碑。然而&#xff0c;据韩国《今日财经》报道&#xff0c;现代重工工会在今年的劳资谈判中提出了一个令人关注的要求——“拒绝晋升权”。这一要求反映了韩国职场的新趋势&#xff0c;即越来越多的员工对高薪…

长期保存红酒的挑战与应对策略

云仓酒庄雷盛红酒&#xff0c;以其卓着的品质和口感&#xff0c;赢得了无数葡萄酒爱好者的喜爱。然而&#xff0c;对于那些希望长期保存这些珍贵佳酿的人来说&#xff0c;如何确保红酒的品质和风味不受时间的影响&#xff0c;却是一项充满挑战的任务。 长期保存红酒的大挑战来自…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 火星字符串(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

电影《加菲猫家族》观后感

上周看了电影《加菲猫家族》&#xff0c;本片其中有很多明亮的画面&#xff0c;相关艳丽的色彩&#xff0c;充满温馨的场景&#xff0c;很符合加菲猫的一贯画风&#xff0c;即使反派出场时&#xff0c;带有阴暗的感觉&#xff0c;看起也不是特别吓人&#xff0c;比较欢乐气氛&a…

定时器介绍之8253芯片

目录 定时器简介 8253功能介绍 组成 工作原理 相关引脚 启动方法 计数方式 实现 读取计数值 定时器简介 8253功能介绍 内部结构 相关引脚 计数器组成 工作原理 启动方法 计数方式 初始化&#xff1a;写入控制字——>写入计数初值 实现 计数长度选择&#xff1a…

虚拟机Ping不通主机

1.问题描述 虚拟机IP&#xff1a; 192.168.3.133 主机ip&#xff1a;192.168.3.137 虚拟机Ping不通主机 主机可以ping通虚拟机 2.解决方案 设置桥接模式 控制面板找到网络和Internet设置 3.问题解决