栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

groovy测试框架-Spock入门

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

groovy测试框架-Spock入门

目录

简介

为什么要用Spock  

一、入门

1.1 依赖引入

1.2.定义一个Spock测试类

1.3一个简单的测试方法 

1.4With与VerifyAll

二、Mock

三、参考文献


简介

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

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

一、入门

1.1 依赖引入
        
            org.spockframework
            spock-core
            1.0-groovy-2.4
            test
        
        
            org.codehaus.groovy
            groovy
            2.4.12
            test
        
        
            org.codehaus.groovy
            groovy-all
            2.4.12
            pom
            test
        
         
            cglib
            cglib-nodep
            3.1
            test
        

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 data="testseeee"

    
    void setup(){
        data="aaaaa"
    }

    
    def "use when"(){
        //初始化数据
        given:"initData"
        def a=1
        def b=2

        //运行
        when:"exec"
        def x=Math.max(a,b)

        //校验
        then:"assert"
        x==2
        data=="aaaaa"
    }

    def "use expect"(){
        
        expect:""
        Math.max(1,2)==2
    }

    
    def "stackExceptionCatch"(){
        given:"initData"
        def stack=new Stack()

        when:"exec"
        stack.pop()

        then:"assert"
        def e=thrown(EmptyStackException)
        e.cause==null

    }

    
    def "math max use where"(){

        expect:"exec"
        c==Math.max(a,b)

        where:"assert"
        a|b||c
        1|2||2
        3|2||3
    }
}

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

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

块名作用说明
given输入条件(前置参数)前面不能有其他块,也不能重复。一个given块不具有任何特殊的语义。该given:标签是可选的并且可以省略,导致隐式 given块。最初,别名setup:是首选的块名称,但使用given:通常会导致更易读的功能方法描述(参见规范作为文档)。
when执行行为在when和then块总是一起出现。他们描述了一种刺激和预期的反应。虽然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 >= 2333
    ram >= 406
    os == "Linux"
  }
}

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

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

def "offered PC matches preferred configuration"() {
  when:
  def pc = shop.buyPc()

  then:
  verifyAll(pc) {
    vendor == "Sunny"
    clockRate >= 2333
    ram >= 406
    os == "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()>>name

        when:"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 的模拟框架可以轻松地仅描述与交互相关的内容,避免过度规范的陷阱。

三、参考文献

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

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

Spock官方文档:Spock framework Reference documentation

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/644061.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号