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
方法,直接使用_
下劃線便可。