简介

为什么要用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. ADPRL - 近似动态规划和强化学习 - Note 7 - Approximate Dynamic Programming
  2. Codeforces Round #276 (Div. 2)
  3. 英伟达首次实现SDF实时3D渲染,还是细节超清晰的那种
  4. 让数据库变快的10个建议
  5. 科大星云诗社动态20210425
  6. 推荐10个好用到爆的Jupyter Notebook插件,让你效率飞起
  7. ABAP正则表达式 vs SPLIT INTO
  8. 使用 Powershell 远程连接 windows server
  9. 为了支持AOP的编程模式,我为.NET Core写了一个轻量级的Interception框架[开源]
  10. perl 序列化_对Perl的热爱团结了多元化的社区
  11. Soul缘何能成为Z时代的心头好?
  12. flash电脑安装包_一百余款电脑软件及安装方式,忍不住收藏起来
  13. 大话移动通信(第2版)!(文末赠书福利)
  14. ehcache 官网下载页面
  15. Unity3D编辑器扩展--自定义创建圆锥体
  16. 计算机学报在线阅读,含指针程序的单子切片方法-计算机学报.pdf
  17. 让电脑键盘L键变成锁定计算机怎么办,教你电脑键盘按键错乱怎么恢复
  18. 【机器学习】一文读懂正则化与LASSO回归,Ridge回归
  19. 1.1.2.3.6nbsp;近距离作战与战术包围
  20. 如何快速创建vue的SPA项目

热门文章

  1. 苹果cmsv10主题仿V256模板绿色风格免费影视模板
  2. 职业生涯规划-职引生涯规划测评
  3. moco系列学习总结
  4. vscode代码片段
  5. linux系统网络邻居,5、Linux下的网络邻居Samba
  6. 测试你的智力,大家来玩很好玩的跳青蛙
  7. 搜索引擎营销你知道吗?
  8. android recyclerview 水平,使用RecyclerView实现水平列表
  9. 设计模式——1.前言
  10. 计算机是如何思考生鲜连锁店选址问题的?