spock框架进行单元测试的学习与实践

单元测试

一个稳定的系统少不了单元测试,单元测试(又称为模块测试, Unit Testing)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。
对于面向对象编程,最小单元就是方法,包括父类、抽象类、或者子类中的方法。
所以单元测试的特点:

  • 测试的是一个代码单元内部的逻辑,而不是各模块之间的交互。
  • 无依赖,不需要实际运行环境就可以测试代码。
  • 运行效率高,可以随时执行。

而单元测试的应用场景一般主要有这几个:

  • 测试驱动开发,由于工期限制,几乎无法实现
  • 对每次代码修改做回归测试

spock测试框架

简介

Spock是基于JUnit的单测框架,提供一些更好的语法,结合Groovy语言,可以写出更为简洁的单测,它的语法完全遵循 BDD(行为驱动开发) 风格的结构。同时集成了像 Jmock、Mockito 等语法表达风格于一体,使得它很简洁有很高的可读性

与其他单元测试框架对比

Spock相比JUnit有易读、简洁、自带Mock等特性,可以减少单元测试编写时间,而且bug更少,可读性更好,写出的单元测试代码更优雅,更易于阅读!
Spock提供了覆盖Java企业应用的完整的测试生命周期。其他框架,是基于某种需要产生的,比如:Junit单纯用于单测,但不提供mock;Mockito提供mock功能,但不提供静态类的mock;PowerMock提供静态类mock;
它们之间需要整合,且不容易跟上新的测试场景。Spock提供了全家桶测试,内置了mock和stub功能等,很方便上手

特点

  • 全能,高效,代码简介,易读,方法名支持中文
  • 提供Groovy的closures,类似Java8的lambda表达式
  • Mockito对参数匹配有限制。如果在方法中使用匹配器,那么所有的参数都需要匹配器。Spock不受此限制,可以将实参与实参匹配器混合和匹配
  • Spock区分了Stub和Mock,让单测更易读
  • Spock提供了更多详细的错误信息

基础

关于依赖

依赖如果版本对不上会报各种各样的错,所以最好是根据maven仓库中查看版本,一定要对应上:
比如如果是3.0版本的spock-core,groovy依赖就必须是:

基本用法

需要创建groovy类使用spock,所有的类都要继承Specification类,如下:

class SpockDemoSpec extends Specification{
    /**
     * 在第一个测试方法开始前执行一遍
     */
    def setupSpec() { println "------------ setupSpec: 所有测试开始之前执行 ------------" }

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

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

    /**
     * 最后一个测试方法后执行
     */
    def cleanupSpec() { println "------------ cleanupSpec: 所有测试方法结束之后执行 ------------" }

    def "测试given-expect"() {
        given:
        def a = new Random().nextInt(10)
        def b = 2
        expect:
        println a
        a > b
    }

    def "测试where"() {
        expect:
        result == ++id
        // where表格 左边是参数,右边是测试结果
        where:
        id | result
        10  | 11
        100  | 101
        1000  | 1001
    }

    def "测试given-when-then"() {
        given:
        // 数据准备
        def a = "houzheng"
        when:
        // 条件判断
        boolean flag = a.equals("houzheng")
        then:
        // 期望结果
        flag
    }

    def "测试异常thrown"() {
        when:
        // 此方法会抛出RuntimeException
        throw new RuntimeException("模拟异常");
        then:
        // 接收异常
        def ex = thrown(Exception)
        ex.class.name == "java.lang.RuntimeException"
        ex.getMessage() == "模拟异常"
    }
}

Demo执行结果:

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

springboot中的使用

常用注解

项目中实际使用

笔者目前项目中某个业务的使用

与原来单元测试代码的对比

打包运行

测试报告输出

心得体会与总结

  • 兼容性比较差,依赖版本稍微不对,就会报一些莫名其妙的错误,而且提示也不明显,不太好排查
  • 其实虽然说要懂groovy,但是懂java的人还是非常容易上手的,只要给一个demo,基本就能够写单测了
  • 如果还想写的更优雅更高级,那可以去学学groovy,这里放一下笔者的链接:https://www.cnblogs.com/houzheng/p/15183586.html , 有兴趣可以看看
posted @ 2022-01-09 16:10  侯小厨  阅读(875)  评论(0编辑  收藏  举报
Fork me on Gitee