Spock單元測試框架實戰指南十 - 注意事項

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-junit5groovy-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能夠參考官方提供的解決方案:

image

(https://github.com/spockframework/spock/commit/fa8bd57cbb2decd70647a5b5bc095ba3fdc88ee9)

後續我也會優先在個人博客(www.javakk.com)推出 Spock2.x 版本的使用教程

建立單元測試文件

編譯(mvn clean compile)經過以後,用spock編寫的groovy類型的單測代碼不能放在原來的test/java目錄下面

由於按照groovy的約定,默認編譯groovy包下的單測,因此須要建個groovy文件夾存放spock的單測代碼,以下圖所示:

image

這樣也方便區分原來Java單測和用Spock寫的單測代碼

另外記得別忘了標記groovy目錄爲測試源目錄(Test Source Root),以下圖:
image

(groovy文件夾右鍵 → Mark Directory as → Test Sources Root)

第一次運行spock單測代碼時若是提示"no test suite exist"的錯誤,能夠右鍵recompile下

還有記得建立的單測文件類型是Groovy Class,不是Java Class類型

image

最後使用intellij idea的快捷鍵建立單元測試,在須要測試的類或方法上右鍵IDE的菜單,選擇"Go To → Create New Test" 選擇咱們已經建立好的groovy文件夾:

image
image
image

這樣就自動生成了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 mockmock方法, 方法參數須要匹配的, 注意不要引用了spock的any()方法, 而應該使用power mock的any方法, 兩者不能混用, 不然會報錯

正確引用路徑org.mockito.ArgumentMatchers

image

錯誤引用路徑org.codehaus.groovy.runtime.DefaultGroovyMethods

image

記得前提是在powermock的api裏使用參數匹配,若是是spock的mock方法,直接使用_下劃線便可。

文章來源:http://javakk.com/322.html

相關文章
相關標籤/搜索