当前位置:   article > 正文

Groovy+Spock单元测试_spock测试模板

spock测试模板

一、导入依赖

Spock是基于JUnit的单测框架,提供一些更好的语法,结合Groovy语言,可以写出更为简洁的单测。

<!-- groovy依赖 -->
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>2.4.0</version>
</dependency>
<!-- spock核心依赖 -->
<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-core</artifactId>
    <version>1.3-groovy-2.4</version>
    <scope>test</scope>
</dependency>
<!-- spring spock依赖 -->
<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-spring</artifactId>
    <version>1.3-groovy-2.4</version>
    <scope>test</scope>
</dependency>
<!-- 单元测试依赖 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

二、测试例子

继承Specification

package com.qiang.groovy.controller

import com.qiang.groovy.controller.ConfigInfoController
import spock.lang.Specification

class ConfigInfoControllerGroovy extends Specification {
    
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

固定方法

/**
 * 在第一个测试方法开始前执行一遍
 */
def setupSpec() {
    println "------------ setupSpec()方法 ------------"
}

/**
 * 每个测试方法开始前都会执行一遍
 */
def setup() {
    println "------------ setup()方法 ------------"
}

/**
 * 每个测试方法后都会执行一遍
 */
def cleanup() {
    println "------------ cleanup()方法 ------------"
}

/**
 * 最后一个测试方法后执行
 */
def cleanupSpec() {
    println "------------ cleanupSpec()方法 ------------"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

测试例子

def "测试a>b"() {
  given:
    def a = new Random().nextInt(10)
    def b = 2
  expect:
    println a
    a > b
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

点击运行

image-20210415004728353

测试通过

image-20210415004802675

测试不通过

image-20210415004841948

三、基本构造

  • where: 以表格的形式提供测试数据集合
  • when: 触发行为,比如调用指定方法或函数
  • then: 做出断言表达式
  • expect: 期望的行为,when-then的精简版
  • given: mock单测中指定mock数据
  • thrown: 如果在when方法中抛出了异常,则在这个子句中会捕获到异常并返回
  • def setup() {} :每个测试运行前的启动方法
  • def cleanup() {} : 每个测试运行后的清理方法
  • def setupSpec() {} : 第一个测试运行前的启动方法
  • def cleanupSpec() {} : 最后一个测试运行后的清理方法

四、构造例子

4.1 expect-where

在where子句中以表格形式给出一系列输入输出的值,然后在expect中引用。

@Unroll
def "测试expect-where"() {
    expect:
    	userInfoService.getById(id).getName() == name
    where:
    	id | name
        1  | "小强"
        2  | "傻狗"
        3  | "小猪"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

4.2 given-when-then

当条件满足时,达到期望的结果。

@Unroll
@Transactional
def "测试given-when-then"() {
    given:
    // 数据准备
    UserInfo userInfo = new UserInfo()
    userInfo.setName("小强崽")
    userInfo.setAsset(10000000.00)
    when:
    // 条件判断
    boolean flag = userInfoService.save(userInfo)
    then:
    // 期望结果
    flag
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

4.3 when-then-thrown

测试异常信息。

/**
* 模拟异常
*/
@Override
public void getExceptionMessage() {
    throw new RuntimeException("模拟异常");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

测试方法。

def "测试异常thrown"() {
    when:
    // 此方法会抛出RuntimeException
    userInfoService.getExceptionMessage()
    then:
    // 接收异常
    def ex = thrown(Exception)
    ex.class.name == "java.lang.RuntimeException"
    ex.getMessage() == "模拟异常"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

五、远程挡板

远程调用第三方服务需要Mock挡板,避免受第三方服务的影响。

远程调用服务

package com.qiang.groovy.controller;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qiang.common.response.ResponseResult;
import com.qiang.groovy.entity.UserInfo;
import com.qiang.groovy.feign.GiteeServiceFeign;
import com.qiang.groovy.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.Serializable;
import java.util.List;

/**
 * @author 小强崽
 * @create: 2021-04-11 21:31:14
 * @description: 控制层
 */
@RestController
@RequestMapping("/user/info")
public class UserInfoController {
	/**
     * 注入远程调用接口
     */
    @Autowired
    private GiteeServiceFeign giteeServiceFeign;

    /**
     * 远程调用
     *
     * @param msg
     * @return
     */
    @GetMapping("/gitee/test/feign")
    public ResponseResult<String> testFeign(@RequestParam("msg") String msg) {
        return giteeServiceFeign.testFeign(msg);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

远程调用做挡板后,自定义挡板返回的参数即可。

package com.qiang.groovy.controller

import com.qiang.common.response.ResponseResult
import com.qiang.common.util.SpringContextUtil
import com.qiang.groovy.feign.GiteeServiceFeign
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification

@SpringBootTest
class UserInfoControllerGroovy extends Specification {

    @Autowired
    private UserInfoController userInfoController

    @Autowired
    private SpringContextUtil springContextUtil;

    /**
     * 单元测试调用第三方服务时,需要做挡板
     */
    def giteeServiceFeign = Mock(GiteeServiceFeign)

    def "测试远程调用"() {
        given:
        // 定义挡板返回的参数,(*_)为任意入参参数
        giteeServiceFeign.testFeign(*_) >> ResponseResult.success()
        expect:
        userInfoController.testFeign(msg).code == result
        where:
        msg   | result
        "msg" | "200"
    }

    /**
     * 每个测试方法开始前都会执行一遍
     */
    def setup() {
        // 挡板赋值
        userInfoController.giteeServiceFeign = giteeServiceFeign
    }

    /**
     * 每个测试方法后都会执行一遍
     */
    def cleanup() {
        // 还原挡板
        userInfoController.giteeServiceFeign = springContextUtil.getBean(GiteeServiceFeign.class)
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

六、常用注解

6.1 @Unroll

某个测试用例失败了,却难以查到是哪个失败了,这时候,可以使用@Unroll注解,该注解会将where子句的每个测试用例转化为一个 @Test 独立测试方法来执行,这样就很容易找到错误的用例。

@Unroll
def "测试getById()"() {
    expect:
    	userInfoService.getById(id).getName() == name
    where:
    	id | name
        1  | "小强"
        2  | "傻狗"
        3  | "小猪"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

没加@Unroll之前

image-20210421172526968

加了@Unroll之后

image-20210421172739758

6.2 @Transactional

插入数据时,加上该注解测试完成会回滚数据。

image-20210421180516967

作者(Author):小强崽
来源(Source):https://www.wuduoqiang.com/archives/Groovy+Spock单元测试
协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
版权(Copyright):商业转载请联系作者获得授权,非商业转载请注明出处。 For commercial use, please contact the author for authorization. For non-commercial use, please indicate the source.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/760047
推荐阅读
相关标签
  

闽ICP备14008679号