简介

为什么要用Spock

一、入门

1.1 依赖引入

1.2.定义一个Spock测试类

1.3一个简单的测试方法

1.4With与VerifyAll

二、Mock

三、Mock,stub,spy

四、参考文献


简介

Spock 是用于 Java 和 Groovy 应用程序的测试和规范框架。使它从人群中脱颖而出的是其美丽且极具表现力的规范语言。由于其 JUnit 运行器,Spock 与大多数 IDE、构建工具和持续集成服务器兼容。Spock 的灵感来自JUnit、 jMock、RSpec、Groovy、Scala、 Vulcans和其他迷人的生命形式。

为什么要用Spock

总的来说,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.1 依赖引入

        <dependency><groupId>org.spockframework</groupId><artifactId>spock-core</artifactId><version>1.0-groovy-2.4</version><scope>test</scope></dependency><dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy</artifactId><version>2.4.12</version><scope>test</scope></dependency><dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy-all</artifactId><version>2.4.12</version><type>pom</type><scope>test</scope></dependency><dependency> <!-- enables mocking of classes (in addition to interfaces) --><groupId>cglib</groupId><artifactId>cglib-nodep</artifactId><version>3.1</version><scope>test</scope></dependency>

1.2.定义一个Spock测试类

创建类的时候选择Groovy Class

class MyFirstSpec extends Specification {
....}

Specification包含许多用于编写规范的有用方法。此外,它指示 JUnit 使用SputnikSpock 的 JUnit运行器运行规范。多亏了 Sputnik,大多数现代 Java IDE 和构建工具都可以运行 Spock 规范。

我们在给测试类命名时通常以Specification或Spec结尾,以标识出类为Spock测试类。

常用方法介绍:

def setupSpec() {} // 运行一次 - 在第一个def方法运行之前setup() {} // 在每个def方法运行之前运行cleanup() {} // 在每个def方法运行之后运行cleanupSpec() {} // 运行一次 - 在最后一个def方法运行之后

Junit与Spock方法的一个映射关系

1.3一个简单的测试方法

class MyFirstSpec extends Specification {/*** @Shared 将变量定义为共享变量*/@Shared data="testseeee"/*** 在每个def方法运行前给data赋值*/void setup(){data="aaaaa"}/*** 测试方法* @return*/def "use when"(){//初始化数据given:"initData"def a=1def b=2//运行when:"exec"def x=Math.max(a,b)//校验then:"assert"x==2data=="aaaaa"}def "use expect"(){/*** 运行并校验*/expect:""Math.max(1,2)==2}/*** 异常捕获,相比较与try-catch,spock中有了新的异常捕获方式* thrown()* 通过使用notThrown(),表示不应抛出的异常*/def "stackExceptionCatch"(){given:"initData"def stack=new Stack()when:"exec"stack.pop()then:"assert"def e=thrown(EmptyStackException)e.cause==null}/*** where块的使用,可用于数据驱动的测试* @Unroll 对于where的每个情况都生成一个单测用例,不加该注解则认为这两种情况只是一个单测示例* #a |#b ||#c 用于输出参数值,让单测示例更易看*/@Unrolldef "math max use where #a |#b ||#c"(){expect:"exec"c==Math.max(a,b)where:"assert"a|b||c1|2||23|2||3}
}

Spock 内置支持实现功能方法的每个概念阶段。为此,特征方法被构造成所谓的。块以标签开始,并延伸到下一个块的开头,或方法的结尾。有6种模块:givenwhenthenexpectcleanup,和where块。方法开头和第一个显式块之间的任何语句都属于隐式given块。

下图演示了块如何映射到特征方法的概念阶段。该where区块有一个特殊的作用,很快就会揭晓。但首先,让我们仔细看看其他块。

块名 作用 说明
given 输入条件(前置参数) 前面不能有其他块,也不能重复。一个given块不具有任何特殊的语义。该given:标签是可选的并且可以省略,导致隐式 given块。最初,别名setup:是首选的块名称,但使用given:通常会导致更易读的功能方法描述(参见规范作为文档)。
when 执行行为 whenthen块总是一起出现。他们描述了一种刺激和预期的反应。虽然when 块可以包含任意代码,但then块仅限于条件异常条件交互和变量定义。一个特征方法可能包含多对when-then块。
then 输出条件,验证结果
and 衔接上个标签,补充作用
expect
类似when+then的结合 一个expect块被比较有限then的,因为它可能只包含条件和变量定义块。在更自然地用单个表达式描述刺激和预期反应的情况下,它很有用
cleanup 释放特性方法使用的任何资源

一个cleanup块后面只能跟一个where块,不能重复。与cleanup方法一样,它用于释放特性方法使用的任何资源,即使特性方法(的前一部分)产生了异常,它也会运行。因此,cleanup必须对块进行防御性编码;在最坏的情况下,它必须优雅地处理特征方法中的第一条语句抛出异常的情况,并且所有局部变量仍然具有其默认值。

对象级规范通常不需要cleanup方法,因为它们消耗的唯一资源是内存,垃圾收集器会自动回收内存。然而,更粗粒度的规范可能使用cleanup 块来清理文件系统、关闭数据库连接或关闭网络服务。

where 使用不同的输入和预期结果多次执行相同的测试代码,主要用于数据驱动的测试

1.4With与VerifyAll

def "offered PC matches preferred configuration"() {when:def pc = shop.buyPc()then:with(pc) {vendor == "Sunny"clockRate >= 2333ram >= 406os == "Linux"}
}

您可以使用一种with(target, closure)方法与正在验证的对象进行交互,当pc对象为null时会抛出异常。这在thenexpect块中特别有用。

正常期望在第一个失败的断言上无法通过测试。有时在测试失败之前收集这些失败以获得更多信息是有帮助的,这种行为也称为软断言。该verifyAll方法可以像这样使用with

def "offered PC matches preferred configuration"() {when:def pc = shop.buyPc()then:verifyAll(pc) {vendor == "Sunny"clockRate >= 2333ram >= 406os == "Linux"}
}

二、Mock

在我们测试的过程中很多资源是无法获取或者说无法直接使用的,比如我们调用一个三方的接口,其实我们是无法确认对方的返回内容永不变动的,但是这种不稳定因素就会对我们的测试结果产生影响,所以就需要我们自己去模拟一些对象方法调用或接口的返回,Mock就此登场。就我个人使用而言感觉Mock与@MockBean的作用是相似的。

class PublisherSpec extends Specification {//模拟接口def testMock=Mock(TestMock.class)def room=new Room(testMock:testMock)def "test mock"(){given:"initData"def name="张三"and:"mock"//模拟接口调用的返回testMock.getName()>>namewhen:"exec"def result=room.get(1)then:"assert"assert result=="张三"}}

测试相关类及接口定义:

public class Room {public TestMock testMock;public String get(Integer index){return testMock.getName();}}
public interface TestMock {String getName();}

在这里尽管TestMock接口并没有对应的实现类,但是我们还是可以使用该类的方法, 这是因为与大多数 Java模拟框架一样,Spock 使用 JDK 动态代理(模拟接口时)和Byte Buddy或CGLIB代理(模拟类时)在运行时生成模拟实现。

与 Mockito 一样,我们坚信模拟框架默认应该是宽松的。这意味着对模拟对象的意外方法调用(或者,换句话说,与手头测试无关的交互)被允许并以默认响应回答。相反,像 EasyMock 和 JMock 这样的模拟框架在默认情况下是严格的,并且会为每个意外的方法调用抛出异常。虽然严格强制严格,但它也可能导致过度规范,导致脆弱的测试在每次其他内部代码更改时失败。Spock 的模拟框架可以轻松地仅描述与交互相关的内容,避免过度规范的陷阱。

三、Mock,stub,spy

stub

stub只是简单的生成一个目标类的代理类,关注重点为方法的返回,对于方法执行的次数等不关注。

def venderWorkOrderCmdRpc = Stub(VenderWorkOrderCmdRpc)

Mock

在stub的基础上有了方法执行次数的关注

given:
subscriber.receive("message1") >> "ok"when:
publisher.send("message1")then:
1 * subscriber.receive("message1")  //标识该方法应该只执行一次  ,0*标识一次也不执行

如果stub的对象添加方法执行次数的判断会抛出InvalidSpecException的异常。

spy

spy总是基于真实的对象。因此,必须提供类类型而不是接口类型,以及该类型的任何构造函数参数。如果未提供构造函数参数,则将使用该类型的无参数构造函数。

WorkOrderCreateCheckAbilityImpl workOrderCreateCheckAbility = Spy()

四、参考文献

Spock美团技术实践总结:Spock单元测试框架介绍以及在美团优选的实践 - 美团技术团队

Spock单元测试框架保姆级教程:https://javakk.com/category/spock/page/2

Spock官方文档:Spock Framework Reference Documentation

groovy测试框架-Spock入门相关推荐

  1. BDD测试框架Spock概要

    前言 为了找到一个适合自己的.更具操作性的.以DDD为核心的开发方法,我最近一直在摸索如何揉合BDD与DDD.围绕这个目标,我找到了Impact Mapping → Cucumber → Spock ...

  2. 下一代CC++测试框架TestNG++入门指导【转】

    原文:http://www.cnblogs.com/sinojelly/archive/2010/05/22/1741646.html   xUnit框架改变了单元测试的历史,一时间,很多语言的多种单 ...

  3. spock测试框架使用指北

    文章目录 一.Spock是什么 二.Spock,Junit,Jmock以及PowerMock区别 三.Spock项目引用配置 1. POM版本依赖 2. 新建测试用例 3. 执行单元测试 四.Spoc ...

  4. Spock测试框架如何Mock静态方法

    1. 问题场景 在写单元测试时,难免会遇到需要Mock的静态方法.当使用Spock测试框架时,Spock提供Moc静态方法Mock只支持Groovy语言,无法支持Java语言实现的静态方法Mockin ...

  5. spring框架mvc框架_Spring的MVC测试框架入门–第1部分

    spring框架mvc框架 最新推出的主要Spring框架是Spring MVC测试框架,Spring Guys声称它是"一流的JUnit支持,可通过流畅的API测试客户端和服务器端Spri ...

  6. spring框架mvc框架_Spring MVC测试框架入门–第2部分

    spring框架mvc框架 这个迷你系列的第一个博客介绍了Spring MVC测试框架,并演示了其在单元测试Spring MVC Controller类中作为控制器而不是POJO进行单元测试的用途. ...

  7. Spring MVC测试框架入门–第1部分

    最新推出的主要Spring框架是Spring MVC测试框架,Spring Guys声称它是"一流的JUnit支持,可通过流畅的API测试客户端和服务器端Spring MVC代码" ...

  8. Spring MVC测试框架入门–第2部分

    这个迷你系列的第一个博客介绍了Spring MVC测试框架,并展示了其在单元测试Spring MVC Controller类中作为控制器而不是POJO进行单元测试的用途. 现在是时候讨论使用框架进行集 ...

  9. Spock测试框架浅尝

    文章目录 0. 写在前面 1. 为什么要进行单元测试? 1.1 什么是单元测试? 1.2 单元测试的几个好处 2. Spock框架介绍 2.1 规范 Specification 夹具方法 Fixtur ...

最新文章

  1. Java垃圾回收之新生代垃圾收集器
  2. Python_sqlalchemy之多对多建表
  3. SpringBoot从入门到实战只需一篇文章
  4. python数据科学-数据预处理
  5. win7系统出现蓝屏0x0000003b怎么解决
  6. C#多线程学习(一) 多线程的相关概念
  7. total video converter 绿色_志愿服务清理杂草 牵手绿色生态文明
  8. 程序员过关斩将--你的业务是可变的吗
  9. weixin-java-tools工具-微信开发常见问题
  10. My Thirty-fifth Page - 最大二叉树 - By Nicolas
  11. 【不忘初心】Windows11 22000.168 X64 四合一[纯净精简版][2.77G](2021.8.29)
  12. 2029年会实现通用人工智能吗?
  13. Linux IP代理筛选系统(shell+proxy)
  14. [19保研]清华大学交叉信息研究院优秀大学生夏令营
  15. matlab矩阵转入tecplot,[转载]tecplot编辑自己想要的变量
  16. V8引擎如何回收内存以及如何优化
  17. 画思维导图的注意事项有哪些?怎样画思维导图?
  18. ios QQ登录已经安装QQ客户端但仍然提示下载QQ,您没有安装最新版本qq
  19. 设计一个算法求象棋这些棋子各代表哪些数字
  20. 92天倒计时,蓝桥杯省赛备赛攻略来啦~

热门文章

  1. 问题 1498: [蓝桥杯][算法提高VIP]凶手(C++)
  2. Scrapy框架爬取名人名言网信息——基于css内置选择器
  3. 一码多付-------支付宝/微信扫码支付(H5支付) 静态码
  4. java 语法糖 字符串,java中的一些语法糖
  5. Django与Channels实现WebSocket
  6. python双目三维重建_OpenCV+OpenGL 双目立体视觉三维重建
  7. 肺结节公开数据集(LIDC-IDRI)
  8. Nextracker上市:市值45亿美元 为美股今年最大IPO
  9. 聚类算法-层次(系统)聚类 Kmeans聚类 两步聚类
  10. 详解统计信号处理之克拉美罗界