Spock1

2023-11-13


参考资料:https://juejin.cn/post/6844903957475622926#heading-21

背景:

Spock是Java和Groovy应用程序的测试和规范框架

测试代码使用基于groovy语言扩展而成的规范说明语言(specification language)

通过junit runner调用测试,兼容绝大部分junit的运行场景(ide,构建工具,持续集成等)

扩展:

groovy:是以扩展java为目的为设计的JVM语言,可以使用java语法和api,广泛应用于:jenkins,elasticsearch,gradle

BDD:Behavior-driven development行为驱动测试

通过某种规范说明语言去描述程序“应该”做什么,再通过一个测试框架读取这些描述、并验证应用程序是否符合预期。把需求转化成Given/When/Then的三段式

依赖

<dependency>
	<groupId>org.codehaus.groovy</groupId>
	<artifactId>groovy-all</artifactId>
	<version>2.4.12</version>
</dependency>

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.12</version>
	<scope>test</scope>
</dependency>

<dependency>
	<groupId>org.spockframework</groupId>
	<artifactId>spock-core</artifactId>
	<version>1.1-groovy-2.4</version>
	<scope>test</scope>
</dependency>	

Demo

public interface CacheService {

    String getUserName();
}
public class Calculator {

    private CacheService cacheService;

    public Calculator(CacheService cacheService) {
        this.cacheService = cacheService;
    }

    public boolean isLoggedInUser(String userName) {
        return Objects.equals(userName, cacheService.getUserName());
    }
    ...
}
class CalculatorSpec extends Specification {
    
    // mock对象
//    CacheService cacheService = Mock()
    def cacheService = Mock(CacheService)
    def calculator
    void setup() {
       calculator = new Calculator(cacheService)
    }
    def  "is username equal to logged in username"() {
        // stub 打桩
        cacheService.getUserName(*_) >> "Richard"
        when:
        def result = calculator.isLoggedInUser("Richard")
        then:
        result
    }
    ...
}

Spock深入

在Spock中,待测系统(system under test; SUT) 的行为是由规格(specification) 所定义的。在使用Spock框架编写测试时,测试类需要继承自Specification类。命名遵循Java规范。

结构:

每个测试方法可以直接用文本作为方法名,方法内部由given-when-then的三段式块(block)组成。除此以外,还有andwhereexpect等几种不同的块。

@Title("测试的标题")
@Narrative("""关于测试的大段文本描述""")
@Subject(Adder)  //标明被测试的类是Adder
@Stepwise  //当测试方法间存在依赖关系时,标明测试方法将严格按照其在源代码中声明的顺序执行
class TestCaseClass extends Specification {  
  @Shared //在测试方法之间共享的数据
  SomeClass sharedObj
  def setupSpec() {
    //TODO: 设置每个测试类的环境
  }
  def setup() {
    //TODO: 设置每个测试方法的环境,每个测试方法执行一次
  }
  @Ignore("忽略这个测试方法")
  @Issue(["问题#23","问题#34"])
  def "测试方法1" () {
    given: "给定一个前置条件"
    //TODO: code here
    and: "其他前置条件"
    expect: "随处可用的断言"
    //TODO: code here
    when: "当发生一个特定的事件"
    //TODO: code here
    and: "其他的触发条件"
    then: "产生的后置结果"
    //TODO: code here
    and: "同时产生的其他结果"
    where: "不是必需的测试数据"
    input1 | input2 || output
     ...   |   ...  ||   ...   
  }
 
  @IgnoreRest //只测试这个方法,而忽略所有其他方法
  @Timeout(value = 50, unit = TimeUnit.MILLISECONDS)  // 设置测试方法的超时时间,默认单位为秒
  def "测试方法2"() {
    //TODO: code here
  }
 
  def cleanup() {
    //TODO: 清理每个测试方法的环境,每个测试方法执行一次
  }
 
  def cleanupSepc() {
    //TODO: 清理每个测试类的环境
  }


setup与given

 def  "is username equal to logged in username"() {
        setup://习惯于携程given:
        def str = "Richard"
        // stub 打桩
        cacheService.getUserName(*_) >> str
        when:
        def result = calculator.isLoggedInUser("Richard")
        then:
        result
    }

when_then与wxpect

when:
def x = Math.max(1, 2)  
then:
x == 2
//等价于
expect:
Math.max(1, 2) == 2  

assert

条件类似junit中的assert,就像上面的例子,在then或expect中会默认assert所有返回值是boolean型的顶级语句**。**如果要在其它地方增加断言,需要显式增加assert关键字

异常断言:

  def "peek"() {
    when: stack.peek()
    then: thrown(EmptyStackException)
  }
//如果要验证没有抛出某种异常,可以用notThrown()

Mock

创建对象
def subscriber = Mock(Subscriber)
def subscriber2 = Mock(Subscriber)
    
Subscriber subscriber = Mock()
Subscriber subscriber2 = Mock()    

注入对象
class PublisherSpec extends Specification {
  Publisher publisher = new Publisher()
  Subscriber subscriber = Mock()
  Subscriber subscriber2 = Mock()

  def setup() {
    publisher.subscribers << subscriber // << is a Groovy shorthand for List.add()
    publisher.subscribers << subscriber2
  }

调用频率约束
1 * subscriber.receive("hello")      // exactly one call
0 * subscriber.receive("hello")      // zero calls
(1..3) * subscriber.receive("hello") // between one and three calls (inclusive)
(1.._) * subscriber.receive("hello") // at least one call
(_..3) * subscriber.receive("hello") // at most three calls
_ * subscriber.receive("hello")      // any number of calls, including zero
                                     // (rarely needed; see 'Strict Mocking')

目标约束
1 * subscriber.receive("hello") // a call to 'subscriber'
1 * _.receive("hello")          // a call to any mock object
方法约束
1 * subscriber.receive("hello") // a method named 'receive'
1 * subscriber./r.*e/("hello")  // a method whose name matches the given regular expression (here: method name starts with 'r' and ends in 'e')

参数约束
1 * subscriber.receive("hello")        // an argument that is equal to the String "hello"
1 * subscriber.receive(!"hello")       // an argument that is unequal to the String "hello"
1 * subscriber.receive()               // the empty argument list (would never match in our example)
1 * subscriber.receive(_)              // any single argument (including null)
1 * subscriber.receive(*_)             // any argument list (including the empty argument list)
1 * subscriber.receive(!null)          // any non-null argument
1 * subscriber.receive(_ as String)    // any non-null argument that is-a String
1 * subscriber.receive(endsWith("lo")) // any non-null argument that is-a String
1 * subscriber.receive({ it.size() > 3 && it.contains('a') })
// an argument that satisfies the given predicate, meaning that
// code argument constraints need to return true of false
// depending on whether they match or not
// (here: message length is greater than 3 and contains the character a)

Spy

和Mock的区别是,Spy使用类的原有方法,有打桩则使用打桩方法;Mock使用类的打桩方法,如果没有打桩,则类的所有方法默认都返回null。

//spy的标准是:如果不打桩,默认执行真实的方法,如果打桩则返回桩实现。
List<String> list = new LinkedList<String>();  
List<String> spy = spy(list);  
when(spy.size()).thenReturn(100);  
  
spy.add("one");  
spy.add("two");  
  
assertEquals(spy.get(0), "one");  
assertEquals(100, spy.size());

Stub打桩

subscriber.receive(_) >> "ok"
|          |       |     |
|          |       |     response generator
|          |       argument constraint
|          method constraint
target constraint

如:subscriber.receive(_) >> "ok" 意味,不管什么实例,什么参数,调用 receive 方法皆返回字符串 ok

返回固定值
subscriber.receive(_) >> "ok"
//subscriber.receive(_) >> mockOk()
//private String  mockOk(){
//	return "ok"
//}

返回值序列
subscriber.receive(_) >>> ["ok", "error", "error", "ok"]
动态计算返回值
subscriber.receive(_) >> { args -> args[0].size() > 3 ? "ok" : "fail" }
subscriber.receive(_) >> { String message -> message.size() > 3 ? "ok" : "fail" }

产生副作用
subscriber.receive(_) >> { throw new InternalError("ouch") }

链式响应
subscriber.receive(_) >>> ["ok", "fail", "ok"] >> { throw new InternalError() } >> "ok"
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spock1 的相关文章

  • URL 链接中 #、?、连接符& 分别有什么作用?

    在一个 URL 中可以包含很多的内容 其中不仅仅是包含 26 个英文字母 10 个罗马数字 中文汉字 还可以拥有井号 问号 连接符 等三种最常见的符号 那么这些符号在网站中都有哪些作用呢 文章目录 一 井号 二 问号 三 连接 一 井号 井
  • Qt:十六进制字符串和十六进制互转

    Qt 十六进制字符串和十六进制互转 前言 一 字符串转换十六进制 1 封装函数 2 函数调用示例 二 16进制转换字符串 前言 网上查了不少方式 踩了不少坑 最终这个方式是我目前使用感觉较好的一种 具体出处已经没印象了 这里放出完整代码供大
  • Spring(二)IOC容器的初始化流程

    文章目录 一 Spring 核心容器类 1 1 BeanFactory 1 2 ApplicationContext 1 3 BeanDefinition 二 IOC容器的初始化 2 1 基于Xml的IOC容器的初始化 2 1 1 寻找入口
  • 15个顶级Java多线程面试题及答案

    1 现在有T1 T2 T3三个线程 你怎样保证T2在T1执行完后执行 T3在T2执行完后执行 这个线程问题通常会在第一轮或电话面试阶段被问到 目的是检测你对 join 方法是否熟悉 这个多线程问题比较简单 可以用join方法实现 2 在Ja
  • LLM在放射科学中应用潜力

    本论文在全球范围内评估了 31 个大型语言模型 LLM 在解读放射科报告并从放射学发现中推导出诊断信息 impression 任务上的表现 这是目前已知的对全球 LLM 用于放射科学自然语言处理 NLP 进行的最全面评估之一 该研究通过在这
  • 8款常见的自动化测试开源框架

    在如今开源的时代 我们就不要再闭门造车了 热烈的拥抱开源吧 本文针对性能测试 Web UI 测试 API 测试 数据库测试 接口测试 单元测试等方面 为大家整理了github或码云上优秀的自动化测试开源项目 希望能给大家带来一点帮助 一 性
  • 运维体系的构建

    文章目录 一 前言 二 基础 2 1 项目摸底 2 2 做一个好辅助 2 3 学习业务 2 4 标准与流程 2 5 维护 三 进阶 3 1 系统 服务优化 3 2 工作流程优化 3 3 规矩 3 4 运维管理平台 一 前言 运维的基础工作通
  • php lazy loading,React丨用户体验丨hook版 lazy loading

    我们都知道随着单页应用 bundle 的体积不断增大 会造成首次加载时间过长 白屏时间过长 过程中会加载了我们首页没有必要看到的一些 页面 组件 js文件 所以我们需要对 bundle 文件进行拆分来进行按需加载 懒加载 这里需要用到 we
  • Xilinx FIFO Generator 需要注意RST复位

    Xilinx FIFO Generator 需要注意RST复位 系列文章推荐 Xilinx FIFO Generator 需要注意RST复位 Xilinx FIFO Generator 需要注意Actual Depth Xilinx FIF
  • cvCloneImage()内存泄漏解决方法, cvCloneImage()和cvCopy()的区别

    转自 http blog csdn net stellar0 article details 8741759 cvCloneImage 每次使用时编译器会分配新的内存空间 不会覆盖以前的内容 所以如果在循环中使用内存会迅速减小 每次用完都需
  • Python 计算机视觉(六)—— OpenCV 进行图像量化与采样

    对于信号的采样可以参考我之前的文章 数字信号处理 2 1 采样 对于信号的量化可以参考 数字信号处理 2 4 ADC 中的有限字长效应 在本篇文章中绘图使用到了 matplotlib 库 需要了解学习可以参考我之前写的用来总结这个绘图库的文
  • 一文看懂Spark中reduceByKey 和 groupByKey 的区别

    目录 一 先看结论 二 举例 画图说明 1 实现的功能分别是什么 1 groupByKey 实现 WordCount 2 reduceByKey 实现 WordCount 2 画图解析两种实现方式的区别 1 groupByKey 实现 Wo
  • C++深拷贝与浅拷贝以及写时复制

    深拷贝和浅拷贝的优缺点 看了深拷贝 浅拷贝优缺点 我们知道浅拷贝效率高 但涉及到指针引用等会涉及到指针的多次释放导致悬挂指针 深拷贝 不会造成指针悬挂的问题 但会浪费空间以及效率较低的问题 下面看下用到浅拷贝的情况 include
  • TCP通信发送和接收数据(Socket、ServerSocket)、TCP通信案例

    目录 TCP TCP发送接收数据 发送数据 Socket 接收数据 ServerSocket TCP通信案例1 TCP接收数据后给出反馈案例2 TCP接收数据后给出反馈案例3 TCP接收数据后给出反馈案例4 TCP 概述 TCP通信协议是一
  • C++实现——三子棋游戏

    题目描述 两个人玩三子棋游戏 即在3 3的矩阵上下棋 一个人画叉一个人画圈 谁先出现成行或成列或成对角线三个相同的棋子就算谁赢 编写算法实现 判断给定棋局的状态 用1代表先手 2代表后手 出现的六种状态为 1won 2won x 代表棋局错
  • 爬虫逆向——某建筑市场监管平台的滑块验证码分析

    目录 网址链接 正文 一 思路分析 二 图片处理 三 完整代码 网址链接 aHR0cHM6Ly9nY3htLmh1bmFuanMuZ292LmNuL2RhdGFzZXJ2aWNlLmh0bWw bs64解密可见 正文 注 分步的代码为示例代
  • GD32F103与STM32F103的区别 2021.6.2

    GD32F103和STM32F103区别介绍 关键词Key words GD32F103 STM32F103 摘要Abstract 本文主要是GD32F103和STM32F103区别进行介绍 目录 简介 GD32和STM32的区别 2 1
  • 正交向量 正交矩阵

    如何判断向量正交 内积 对应位置相乘再求和 是内积 卷积 加上滑动窗口 判断向量是否正交 两个向量正交 求其内积 看是否为0 若为零 则正交 在空间上向量垂直就正交 例子 a 1 1 0 b 1 1 0 则内积 a b 1 1 1 1 0
  • Linux教程:如何使用kubeadm从头到尾搭建k8s单节点服务并部署dashboard

    前言 在以往教程中 我们使用的是Minikube快速搭建的k8s服务 但这种方式只能在开发环境中使用 并不推荐生产环境 官方的推荐的方案是采用kubeadm快速搭建 kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工

随机推荐

  • 改造vue-element-admin 的登录功能,变成从后台数据库中验证登录

    改造vue element admin 的登录功能 变成从后台数据库中验证登录 首先了解登录时前段需要什么样的数据 要知道vue element admin 这个后台开发模板是集成非常多我们日常开发网站的基本功能 所以我们在改造登录功能的时
  • Leetcode26-28,这几道简单有趣的算法题你都会吗?

    26 删除排序数组中的重复项 题目要求 给定一个排序数组 你需要在原地删除重复出现的元素 使得每个元素只出现一次 返回移除后数组的新长度 不要使用额外的数组空间 你必须在原地修改输入数组并在使用O 1 额外空间的条件下完成 示例1 给定数组
  • flutter GestureDetector onTop 点击反应过慢

    如果你遇到过需要自己定义的带手势的view 发现点击几下可能只响应一次 很有可能是GestureDetector的child没有加背景 有时GestureDetector的child不需要加背景也可以点击 但是只可以点击child显示有内容
  • 几个常用数学知识点

    机器学习跟数学有着紧密的关系 因此掌握一些常用的数学知识点 有助于我们理解某些模型的底层相关原理 1 泰勒公式 2 驻点 极值点 鞍点 拐点 2 1 驻点 在数学 特别在微积分 函数在一点处的一阶导数为零 该点即函数的驻点 Stationa
  • MyBatis中的statementType详解

    在mapper文件中可以使用statementType标记使用什么的对象操作SQL语句 statementType 标记操作SQL的对象 取值说明 1 STATEMENT 直接操作sql 不进行预编译 获取数据 Statement 2 PR
  • 各种酒英文名

    okolehao 夏威夷烧酒 芋薯烧酒 orgeat 杏仁橘花香茶一种用于鸡尾酒和食物中的 含有桔子与杏仁的甜作料 杏仁糖浆 鸡尾酒的一种配料或食品的香料 不含酒精 杏仁桔花香茶 一种不含酒精的清凉甜饮料 ouzo 茴香烈酒 无色 不甜且有
  • 前端中的滴到思

    hping3 c 200000 d 12000 S w 64 p 801 flood rand source IP c 是数据包数量 d 是数据包的大小 S 是数据包的类型 w winsize 默认 64 p 目标网站的端口 flood 开
  • 解决Windows11系统缺少Windows.Data.Pdf.dll文件出现错误问题

    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题 如果是新手第一时间会认为是软件或游戏出错了 其实并不是这样 其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库 这时你可以下载这个Windows
  • nodeJS fs.writeFile 实现文字换行

    写文件遇见需要换行的情况就在需要换行的内容前面加换行符号就阔以了 简单 r n windows n linux r mac 下面是一个复制文件的方法 每次追加时间戳的时候希望他换行 gt appendFile 的时候内容前面加 r n co
  • php自动验证,ThinkPHP 自动验证及验证规则详解

    ThinkPHP 自动验证 ThinkPHP 内置了数据对象的自动验证功能来完成模型的业务规则验证 自动验证是基于数据对象的 而大多情况下数据对象是基于 POST表单 不是绝对的 创建的 基本的自动验证功能包括 必须字段 email邮箱格式
  • 使用 ChatGPT、Stable Diffusion、React 和 NodeJS 构建网站画廊

    TLDR 在本文中 您将学习如何构建一个 Web 应用程序 该应用程序使用 ChatGPT 和 Stable Diffusion 为您提供的任何网站描述生成徽标和合适的域名 介绍 人工智能正在接管世界 这些技术每天都在震撼着我们的世界 Ch
  • 家里用服务器放在哪个位置,路由器摆放在家中哪个位置好 路由器摆放位置【详解】...

    路由器摆放在家中哪个位置好 路由器的摆放位置其实非常讲究的 这里就给大家讲解下相关知识 一起来看看 其实wifi所发射的信号 也就是无线电波 向手机和收音机发射出的电磁波是一样的 但是呢wifi的信号相当的短 一般常见的话只有12公分左右
  • Windows Server 系列 - User logon name(pre-Windows 2000) 和 User logon name 的区别

    一 在Active Directory中一直疑惑User logon name pre Windows 2000 和 User logon name这两个字段的区别 详细如下 AD UI界面展示名称 AD 后端属性名称 User logon
  • 使用Composition API和setup语法糖重构Vue组件

    Vue3 引入了Composition API 它是一种更灵活的方式来组织和复用组件的逻辑 而不是依赖于传统的选项式API 如data methods computed等 Composition API的核心是一个名为setup的函数 它可
  • 如何在github上重命名或修改文件夹

    在github上整理流程的时候 有一个文件夹命名不合适 想返回去改 但是在网页上没有找到重命名文件夹的选项 经过一番折腾之后 我是这么做的 1 首先在服务器上找到公匙 公匙在 ssh目录下 以 pub结尾的文件 将其复制 2 在github
  • markdown基本用法

    标题 和 都可以用于表示标题 一级标题 二级标题 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 标题的前后都要空一行 号后应当加一个空格 和 应当顶格书写 建议使用 来表示标题 字体 斜体 斜体 加粗 粗体 斜体 加粗 斜体
  • OpenCV中的人脸活体检测和身份认证如何实现?OpenCV人脸识别

    本文将介绍如何在OpenCV中实现人脸活体检测和身份认证 结合人脸检测 关键点定位和深度学习模型 我们可以有效地检测和区分真实人脸和照片 视频等非真实生物特征 以实现可靠的身份认证和活体检测 人脸检测和关键点定位 使用OpenCV提供的人脸
  • [STM32学习笔记(一)] 如何安装keil5 MDK版本并安装C51

    文章目录 1 注意事项 2 安装流程 2 1 获取Keil5安装包 2 2 安装keil5 2 3破解keil5 MDK 2 4 安装STM32芯片包 3 在安装了mdk的基础上安装c51 1 注意事项 安装路径必须全部是英文 如果已经安装
  • 突破前端反调试:阻止页面无限不断debugger

    不知道你们有没有遇到过上图这样 有时候想调试网站 一打开开发者工具立即 debugger 而且跳过了还是会继续 或者是有时候在调试网页时 突然就给你来一个 debugger 接着就是反复来回 debugger 了 贼烦 那今天分享个教程 教
  • Spock1

    文章目录 背景 扩展 BDD Behavior driven development行为驱动测试 依赖 Demo Spock深入 结构 setup与given assert 异常断言 Mock 创建对象 注入对象 调用频率约束 目标约束 方