SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock

能正常启动并使用Spock

  • 前言
  • 对Spock的一言概述
  • 环境
  • 依赖
  • IDEA的准备
    • 插件
    • 项目结构
  • 初次使用
    • 类的结构
    • 一个简单的例子
    • 简单总结
  • 遇到的问题
    • 项目启动或者Build Module的时候控制台报版本不一致
    • Can't find MacroTransformation Or Internal Groovy error : code 1
    • No runnable methods
  • 参考资料

前言

本来打算是把Spock的使用写成一篇的,后来发现太长了而且结构比较冗杂,还是拆分出来比较好,而且这样也比较好检索。初次使用,如果有误,请轻喷,并指出问题。我会及时纠正。
要使用Spock,必须要有Junit和Mock的基本概念和使用。建议在使用Spock之前先懂了解一下Junit和Mockitio
下一篇博客:《SpringBoot+Spock的熟悉之路(二):Spock,Mock和Mockitio的关系》

对Spock的一言概述

简而言之,通过Groovy脚本语言一些具有特色的语法,可以让我们在写测试类的时候写起来更方便,更快,读起来看着代码也能稍微简洁易懂些(当然是对于熟悉这套框架语法的人而言)
对于Java程序员来讲,Groovy语言非常友好,我们甚至可以在Groovy类里完全写java代码(不过这样使用Groovy就没啥意义了)或者混合写代码而不会报错(我还没遇到过)。

环境

一定要注意版本的问题!一定要注意版本的问题!一定要注意版本的问题!

Tool Version
Intellij IDEA 2018.3 Ultimate
SpringBoot 2.0.1
Java 1.8
mybatis-spring-boot 2.0.1
Groovy 2.4.6

依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.0.1</version>
    </dependency>
    <!--服务器配置的是Oracle-->
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc8</artifactId>
        <version>12.2.0.1</version>
    </dependency>
        
    <!---------------Spock必须的------------------->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
	    <groupId>org.spockframework</groupId>
	    <artifactId>spock-core</artifactId>
	    <version>1.3-groovy-2.4</version>
	    <scope>test</scope>
	</dependency>
	<dependency>
	    <groupId>org.spockframework</groupId>
	    <artifactId>spock-spring</artifactId>
	    <version>1.3-RC1-groovy-2.4</version>
	    <scope>test</scope>
	</dependency>
	<dependency>
	    <groupId>org.codehaus.groovy</groupId>
	    <artifactId>groovy-all</artifactId>
	    <version>2.4.6</version>
	</dependency>
	<!--------------  可选项  ----------------->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
	    <groupId>org.projectlombok</groupId>
	    <artifactId>lombok</artifactId>
	    <optional>true</optional>
    </dependency>
    <dependency> <!-- 允许MockInterface等 -->
	   <groupId>net.bytebuddy</groupId>
	   <artifactId>byte-buddy</artifactId>
	   <version>1.9.3</version>
	   <scope>test</scope>
	</dependency>
	<dependency>
	   <groupId>org.objenesis</groupId>
	   <artifactId>objenesis</artifactId>
	   <version>2.6</version>
	   <scope>test</scope>
	</dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

IDEA的准备

插件

可以下一个Spock的Idea插件,让IDEA支持Spock的一些高亮和自动创建等功能
叫这个名字 Spock Framework Enhancements
在这里插入图片描述

项目结构

如果一开始创建SpringBoot项目的时候勾选了test,那么IDEA会自动帮你创建一个test目录。如果想要后续添加test目录的朋友,请按照SpringBoot推荐的结构进行创建——即和main里的项目结构保持高度一致
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第1张图片
resources是我自己新创建的文件夹,之后准备用来存放测试类的配置信息,要让IDEA识别它,点击最上面菜单栏里的File->Project Structure,点击Module,分别把resources标注成Test Resources
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第2张图片
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第3张图片

初次使用

先熟悉一下Spock的语法和结构,单纯的看看Spock特定,熟悉之后再去结合咱们项目的结构
先创建测试类。右键点击new -> Spock Specification。点击那个Groovy Class也可以,只不过需要手动去extends Specification
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第4张图片
自动创建的Spock类如下

package com.example.demo.simple

import spock.lang.Specification

class SimpleTest extends Specification {
     

}

Spock测试类的一大特征就是必须要extends Specification
对比Junit,Spock有一些特点有类似的作用

Spock Junit
Specification Test Class
setup() @Before
cleanup() @After
setupSpec() @BeforeClass
cleanupSpec() @AfterClass
list<<“a”<<“b” list.add(“a”);list.add(“b”)
list.size() == 2 Assert.equals(list.size(),2)

类的结构

一个Spock测试类的结构大体上如下

@Title("测试的标题")
@Narrative("""关于测试的大段文本描述""")
class TestCaseClass extends Specification {
       
  @Shared //在测试方法之间共享的数据
  SomeClass sharedObj
 
  def setupSpec() {
     
    //TODO: 设置每个测试类的环境
  }
 
  def setup() {
     
    //TODO: 设置每个测试方法的环境,每个测试方法执行一次
  }
 
  @Ignore("忽略这个测试方法")
  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: 清理每个测试类的环境
  }

一个简单的例子

实体类

package com.example.demo.entity;

import lombok.Data;

@Data
public class DemoEntity {
     

    private Integer number;

    private String str;

}

Spock测试类

package com.example.demo.simple

import com.example.demo.entity.DemoEntity
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll


class SimpleSpec extends Specification {
     

    //此注解标明该变量可以被下面所有测试类共享
    @Shared
    DemoEntity demoEntity

    def setup(){
     
        demoEntity = new DemoEntity()
    }

    def "simple test1"(){
     
        given:
        demoEntity.number = 1
        def result
        //expect,随处可用的断言,如果这里没有通过,则不会执行下面的when及之后的代码
        expect:
        ++demoEntity.number == 2
        when:
        result = demoEntity.number*2
        then:
        result == 4
    }


    //加了Unroll注解后,where里面的每一条数据是一次测试方法。#str可以动态拼接下面的str值来组成不同的方法名
    @Unroll
    def "simple test2 on check #str"(){
     
        given:
        demoEntity.str = str
        expect:
        check()
        //可以通过where方式来批量定义数据,不仅可以定义属性值,也可以定义方法
        where:
        str| check
        "yes" | {
     return true}
        "no" | {
     return false}
        "0" | {
     throw new Exception("don't support number")}
    }
}

simple test1 好理解,有两次判断,expect那里有一个判断,最后的then那里有一个判断,两次通过后,该条测试方法才算通过
simple test2执行后可以看到控制台的结果是
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第5张图片
看最后的一条的报错是
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第6张图片
可以在类上面加上一个注解@Stepwise它的作用是强制整个测试类按照定义顺序执行。

@Stepwise
class SimpleSpec extends Specification {
     }

换个说法,当点simple test 2的时候,simple test 1也会强制执行
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第7张图片
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第8张图片

简单总结

通过上面的例子,至少可以看出Spock有以下几个特点

  1. 因为基于Groovy,其语法相对来说简单随意,JavaScript那种脚本语言一样
  2. 对测试对象不分访问权限,所有成员变量和成员方法都是可以访问的
  3. 整体可读性较好,哪个模块干什么一目了然,有比较规范的代码结构
  4. 有良好的错误提示,可以很快的定位到问题是出现在哪里
  5. 可以批量进行数据校验,这一点Spock官方文档专门列出了一项,我在stack overflow上也看到了一个帖子,里面举的几个例子挺有趣,可以参考一下
  6. 还有不少有趣的语法,我在之后的博客中会逐渐讨论到并和Mockitio进行对比,目前先列出来官方文档的地址。官方文档中的3,4,5,6项是专门讲语法特点的。其GitHub仓库里的src里也有不少例子可以查看

遇到的问题

本模块主要是把自己踩过的一些比较大的坑给单独列举出来

项目启动或者Build Module的时候控制台报版本不一致

一开始我以为用Groovy的时候一定要在本地装Groovy环境,装了之后发现控制台老实说config文件的第一行被注释过的一行文字有错。虽然此错误不影响程序运行,但对于有强迫症的我来说实在是不爽。
后来查资料说是依赖里的groovy的版本和本地环境的版本不一致。后来就算配成了一样的还是报那个错误。
最后进行尝试发现,虽然要使用Groovy,但我们Java程序员在使用Intellij IDEA的条件下不需要在系统下载并安装Groovy的环境,也不用给Idea设置Groovy的环境变量。 我把本地装的Groovy卸载后,就没有报版本错误的问题。
如果必须要在本地配Groovy的朋友有遇到并解决过这个问题的,麻烦赐教一下怎么解决的

Can’t find MacroTransformation Or Internal Groovy error : code 1

当启动一个Specification的时候,如果控制台出现了这种问题的时候
在这里插入图片描述
在这里插入图片描述
请确认一下自己的Groovy的版本是否为2.5及以上
注意,虽然Groovy现在已经到了2.5了,而且官网上确实也用的是version2.5.7,但是SpringBoot对Groovy2.5的支持貌似有点问题。其结果就是引入了groovy2.5依赖后,groovy编译报internal error:code 1这种不明所以的错误
我目前没有找到比较合适的办法,只能将Groovy回退到2.4版本,如果有找到方法的朋友,麻烦赐教一下,谢谢。
SpringBoot官方issue

No runnable methods

当遇到这种错误的时候
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第9张图片
这个错误是Junit报的,证明这个类并没有被识别为Spock测试类,而是使用的Junit Test或者其他方式启动的。Junit对测试类的识别方法是是否有@Test注解。看一下自己的测试类上是不是加了类似于这种东西
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第10张图片
Spock框架不要这些@Runwith注解。在Specification类里就已经有了此注解
SpringBoot+Spock的熟悉之路(一):能正常启动并使用Spock_第11张图片

参考资料

Spock in Java 慢慢爱上写单元测试
spock-testing-exceptions-with-data-tables
Spock官方文档
Spock开源GitHub

你可能感兴趣的:(代码测试,Spock,Mock,SpringBoot)