Spock让你爱上单元测试
1. 为什么要写单元测试?
首先单元测试是为了提高代码的覆盖率,开发期间就覆盖并测试大部分业务场景,更快的发现和解决问题,提高代码质量,放心的交给其他人调用,从而减少上线前的心理负担。而不是直接交付到测试手中再提bug再打回来再反复测试,既耽误时间又浪费感情。
2. 为什么不想写单元测试?
第一个想到的肯定是麻烦,影响开发速度,本来1天能完成的工作因为要写单元测试可能1天就完不成了,又要mock数据,覆盖场景和用例越多单元测试的代码量也就越大,如果是按方法去写,写到最后可能比实际业务代码量还要多,最重要在这个敏捷为王的时代,确实影响心情,你问我写不写单元测试?我反正不写!
3. 什么是Spock?
Spock 是用于 Java 和 Groovy 应用程序的测试和规范框架,那什么是Groovy?Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,你可以粗糙的理解为它是跑在Jvm上的Python。
而Spock是一款拥有Groovy潇洒写法的单元测试框架,只需要一些依赖即可在java应用中使用groovy语言来编写单元测试。
要学习一门新语言?其实也不是,你只需要学习一点结构体就可以了,因为绝大部分逻辑依然是java,所以几乎没什么学习成本。
4. 和常用的Junit有什么区别?
更简单。并且天生支持mock。
5. 这里使用Springboot-Maven依赖Spock
在Springboot中使用需要依赖SpringbootTest,我这里依赖的是最新版本的Spock 2.0-groovy-3.0,2.0代表Spock版本,3.0是Groovy版本。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--引入spock-->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>2.0-groovy-3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>2.0-groovy-3.0</version>
<scope>test</scope>
</dependency>
还需要依赖一个单元测试插件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
6. 一些小示例
我们需要再单元测试root文件夹下创建Groovy文件而不是之前的java文件
创建一个基本的单元测试文件,加入了Spring上下文,可以直接注入依赖,Spock要求必须继承Specification类
@SpringBootTest(classes = TestApplication.class)
@ContextConfiguration
@ActiveProfiles(profiles = "dev")
class Test extends Specification{
@Autowired
Service service;
}
接下来写几个简单的单元测试,@Unroll注解可以将多次执行单独打印,方便查看结果
@Unroll
def "test add #a + #b = #result"(){
expect: "测试"
service.add(a,b) == result
where: "条件"
a | b | result
1 | 2 | 3
2 | 3 | 6
}
def 用来定义一个方法 ,后面跟上方法名字(不建议中文),expect: 定义了一个期望的行为,也可以理解为在这里调用一些service,我这里定义了一个add函数,返回它们的和
方法名上的# + 参数,是为了打印的时候方便看
service.add(a,b) == result , 意味着你传递了a,b两个参数并且判断结果等于result
where: "条件"
a | b | result
1 | 2 | 3
2 | 3 | 6
where里列举了你需要测试的数据,和断言的结果,我们来执行一次看看
当你提供的数据和结果不符合时,它将提供一个非常友好的错误提示
当我们把返回值修改为正确的时候
其实看到这里,你已经可以开始编写单元测试了,后面会简单再介绍几种写法。
@Unroll
def "test not throw #a + #b"(){
when:
service.add(a, b)
then: "条件"
noExceptionThrown()
where:
a << [1, 2]
b << [2, 3]
}
when then and 替代了上面的 expect, when 调用指定方法或函数 then 断言表达式 and 对上一个标签的补充
where:
a << [1, 2]
b << [2, 3]
对于where他还有这样的写法,不用为了对齐表格而烦恼, 当执行完这两个数据之后没有抛出异常就算通过
反之也可以判断均抛出异常
when:
service.add(a, b)
then:
e = thrown(NullPointerException)
println(e)
where:
a << [null, 2]
b << [1, null]
e << [NullPointerException, NullPointerException]
也可以判断单元测试执行时间不能超过多少
@Unroll
@Timeout(unit = TimeUnit.SECONDS,value = 3)
def "test timeout #a + #b"(){
expect:
service.timeOut() // sleep 5秒
}
@Shared 在多个方法中共享数据
// 执行后执行类似于@After
def cleanup(){
orderId.clear();
}
// 执行前执行类似于@Before
def setup(){
orderId.clear();
}
本文就只浅浅的介绍到这,更多使用方法参考Spock官网