Spock虽然好用,但要应用到实际项目中仍是须要注意几个问题,下面讲下咱们公司在使用过程当中遇到的一些问题和解决方案html
要使用Spock首先须要引入相关依赖,目前使用下来和咱们项目兼容的Spock版本是1.3-groovy-2.5
,以maven为例(gradle能够参考官网),完整的pom依赖以下:java
<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
的依赖git
在引入 groovy-all
包时排除了 groovy-test-junit5
和 groovy-testng
,这两个包和和 power mock 有冲突,在执行 mvn test 会致使NPE的问题github
若是你的项目中没有用过groovy,还须要添加groovy的maven编译插件,这样才能编译咱们用Spock写的单元测试spring
引入groovy依赖后可能会出现版本冲突的问题,由于若是你的项目引用了springboot-start-base这样的集合式jar包,它里面也会引用groovy,有可能跟咱们引入的groovy包版本出现冲突,或者公司的一些框架也会引用groovy的包,若是版本不一致也有可能冲突,须要排下包api
而后执行 mvn clean compile
验证下是否有冲突,若是能成功编译就没有这个问题springboot
目前Spock的最新版本是2.0以上,在Spock 2.x 的版本里官方团队已经移除Sputnik,再也不支持代理运行power mock的方式框架
由于Spock 2.0是基于JUnit5,咱们项目之前的单元测试代码都是基于Junit4编写的,换成Junit5后,须要修改现有的java单测,好比指定代理运行,使用power mock的地方要换成Junit5的扩展语法maven
对现有使用Junit4 + power mock/jmockit的方式改变较大,为下降迁移成本没有使用最新的Spock2.X版本ide
若是你的项目以前就是使用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和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()
的方式代替注解,虽没有注解的语法简洁,但不用再引入额外的依赖
若是在Spock里使用了power mock的mock
方法, 方法参数须要匹配的, 注意不要引用了spock的any()方法, 而应该使用power mock的any方法, 两者不能混用, 不然会报错
正确引用路径org.mockito.ArgumentMatchers
:
错误引用路径org.codehaus.groovy.runtime.DefaultGroovyMethods
:
记得前提是在powermock的api里使用参数匹配,若是是spock的mock
方法,直接使用_
下划线便可。