Spock 尽管好用,但要利用到理论我的项目中还是须要留神几个问题,上面讲下咱们公司在应用过程中遇到的一些问题和解决方案
版本依赖
要应用 Spock 首先须要引入相干依赖,目前应用下来和咱们我的项目兼容的 Spock 版本是1.3-groovy-2.5
,以 maven 为例(gradle 能够参考官网),残缺的 pom 依赖如下:
<spock.version>1.3-groovy-2.5</spock.version>
<groovy.version>2.5.4</groovy.version>
<!-- spock -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>${spock.version}</version>
<scope>test</scope>
</dependency>
<!-- spock 和 spring 集成 -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>${spock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- spock 依赖的 groovy -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<type>pom</type>
<version>${groovy.version}</version>
<exclusions>
<exclusion>
<artifactId>groovy-test-junit5</artifactId>
<groupId>org.codehaus.groovy</groupId>
</exclusion>
<exclusion>
<artifactId>groovy-testng</artifactId>
<groupId>org.codehaus.groovy</groupId>
</exclusion>
</exclusions>
</dependency>
<!--groovy 编译 -->
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compileTests</goal>
</goals>
</execution>
</executions>
</plugin>
Spock 是应用 groovy 语言写单测的,所以须要引入 groovy-all
的依赖
在引入 groovy-all
包时排除了 groovy-test-junit5
和 groovy-testng
,这两个包和和 power mock 有抵触,在执行 mvn test 会导致 NPE 的问题
如果你的我的项目中没有用过 groovy,还须要增加 groovy 的 maven 编译插件,这样能力编译咱们用 Spock 写的单元测试
引入 groovy 依赖后可能会呈现版本抵触的问题,因为如果你的我的项目援用了 springboot-start-base 这样的汇合式 jar 包,它外面也会援用 groovy,有可能跟咱们引入的 groovy 包版本呈现抵触,或者公司的一些框架也会援用 groovy 的包,如果版本不统一也有可能抵触,须要排下包
而后执行 mvn clean compile
验证下是否有抵触,如果能胜利编译就没有这个问题
目前 Spock 的最新版本是 2.0 以上,在 Spock 2.x 的版本里官网团队 曾经移除 Sputnik,不再反对代理运行 power mock 的形式
因为Spock 2.0 是基于 JUnit5,咱们我的项目以前的单元测试代码都是基于 Junit4 编写的,换成 Junit5 后,须要批改现有的 java 单测,比方指定代理运行,应用 power mock 的中央要换成 Junit5 的扩大语法
对现有应用 Junit4 + power mock/jmockit 的形式扭转较大,为升高迁徙老本没有应用最新的 Spock2.X 版本
如果你的我的项目之前就是应用 Junit5 写单测的,那么能够应用 Spock2.X 的版本,2.0 以上版本应用 power mock 能够参考官网提供的解决方案:
(https://github.com/spockframework/spock/commit/fa8bd57cbb2decd70647a5b5bc095ba3fdc88ee9)
后续我也会优先在我的博客 (www.javakk.com) 推出 Spock2.x 版本的应用教程
创立单元测试文件
编译 (mvn clean compile
) 通过之后,用 spock 编写的 groovy 类型的单测代码不能放在原来的 test/java 目录上面
因为依照 groovy 的约定,默认编译 groovy 包下的单测,所以须要建个 groovy 文件夹寄存 spock 的单测代码,如下图所示:
这样也不便辨别原来 Java 单测和用 Spock 写的单测代码
另外记得别忘了标记 groovy 目录为测试源目录(Test Source Root),如下图:
(groovy 文件夹右键 → Mark Directory as → Test Sources Root)
第一次运行 spock 单测代码时如果提醒 ”no test suite exist
“ 的谬误,能够右键 recompile 下
还有记得创立的单测文件类型是 Groovy Class,不是 Java Class 类型
最初应用 intellij idea 的快捷键创立单元测试,在须要测试的类或办法上右键 IDE 的菜单,抉择 ”Go To → Create New Test” 抉择咱们曾经创立好的 groovy 文件夹:
这样就主动生成了 groovy 类型的单测文件了
运行单元测试
执行 mvn test
,依照下面两步的配置保障 spock 单测代码运行胜利后能够执行 mvn clean test
命令,跑一下这个我的项目的单测用例
(这一步不是必须的,但如果公司加了单测覆盖率的统计时,在 cicd 零碎公布时或 merge request to release 代码合并到 release 分支时,会先执行 mvn test
相似的指令,确保所有的单元测试运行胜利)
如果你的我的项目和咱们一样既有 Java 单测又有 Spock 单测,须要确保两种单测都能执行胜利(目前咱们我的项目的 spock 单测和 java 单测在公司的 CICD 零碎以及 git 上都能兼容和通过测试覆盖率要求)
另外依照 Spock 的标准,单测代码文件的命名应该是以 Spec
为后缀的,如果你严格依照这个标准命名单测文件,比方 ”OrderServiceSpec.groovy
“,那么须要在 maven-surefire-plugin 测试插件里增加以 Spec 为后缀的配置:
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<includes>
<include>**/*Spec.java</include>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
然而我是间接应用 IDE 生成单元测试,intellij idea 主动生成的单测后缀还是“Test”,所以不存在这个问题,如果你也是这样,能够疏忽这个问题
单元测试标准
Spock 尽管使用方便,但还是要遵循单元测试的标准来,比方单元测试个别是针对办法或类的维度去测试的,也就是说咱们关注的重点是以后类或办法外部的逻辑
如果以后被测办法依赖了其余层或 module 的逻辑,最好 mock 掉,尽量 不要跨层测试,这属于功能测试或集成测试的领域
比方应用 @SpringBootTest
注解,默认会把以后办法依赖的下一层援用也注入进来,其实齐全能够交给 Spock 去管制,能够不须要SpringBootTest
Spock 和 Mockito 注解混用问题
因为 Spock 并不反对 Mockito 和 power mock 的 @InjectMocks
和@Mock
的组合,运行时会报错,如果你肯定要应用对应的性能能够引入 Mockitio 为 Spock 专门开发的第三方工具:spock-subjects-collaborators-extension
应用 @Subject
和@Collaborator
代替 @InjectMocks
和@Mock
代码如下:
import spock.lang.Specification
import com.blogspot.toomuchcoding.spock.subjcollabs.Collaborator
import com.blogspot.toomuchcoding.spock.subjcollabs.Subject
class ConstructorInjectionSpec extends Specification {
public static final String TEST_METHOD_1 = "Test method 1"
SomeOtherClass someOtherClassNotToBeInjected = Mock()
@Collaborator // 相似于 Mockito 的 @Mock
SomeOtherClass someOtherClass = Mock()
@Subject // 相似于 Mockito 的 @InjectMocks
SomeClass systemUnderTest
def "should inject collaborator into subject"() {
given:
someOtherClass.someMethod() >> TEST_METHOD_1
when:
String firstResult = systemUnderTest.someOtherClass.someMethod()
then:
firstResult == TEST_METHOD_1
systemUnderTest.someOtherClass == someOtherClass
}
class SomeClass {
SomeOtherClass someOtherClass
SomeClass(SomeOtherClass someOtherClass) {this.someOtherClass = someOtherClass}
}
class SomeOtherClass {String someMethod() {"Some other class"}
}
}
具体参考:
https://github.com/marcingrzejszczak/spock-subjects-collaborators-extension
我集体的倡议是用 PowerMockito.mock()
的形式代替注解,虽没有注解的语法简洁,但不必再引入额定的依赖
Power Mock 参数匹配办法 Any()
如果在 Spock 里应用了 power mock 的mock
办法, 办法参数须要匹配的, 留神不要援用了 spock 的 any()办法, 而应该应用 power mock 的 any 办法, 二者不能混用, 否则会报错
正确援用门路org.mockito.ArgumentMatchers
:
谬误援用门路org.codehaus.groovy.runtime.DefaultGroovyMethods
:
记得前提是在 powermock 的 api 里应用参数匹配,如果是 spock 的 mock
办法,间接应用 _
下划线即可。
文章起源:http://javakk.com/322.html