天天看點

Spock單元測試架構系列(二):Spock使用詳解

作者:愛上拉磨的小驢子

在上一篇中,我們介紹了什麼是Spock以及它和JUnit,JMock等其它測試架構的對比,從這一篇開始,我們将開始詳細介紹Spock的使用。

在這一章,我們首先講解如何在項目中引入Spock,以及Spock的一些核心概念介紹,并且抛磚引玉給出一個簡單的單測示例。

引入Spock

  • 環境

針對Java單元測試,開發工具 IntelliJ IDEA。

  • Maven依賴
<!-- 
    可以隻引入這一個包,它已經引入了 spock-core、groovy-all; 如果有沖突可以手動指定版本引入  
    此包是整合springboot使用,用于在spock中啟動springboot容器,
    如果僅想使用spock作為單測使用,可以不引入目前包,隻引入 spock-core、groovy-all 即可
  -->
  <dependency>
      <groupId>org.spockframework</groupId>
      <artifactId>spock-spring</artifactId>
      <version>1.3-groovy-2.4</version>
  </dependency>

<!-- 可以不引入 -->
<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-core</artifactId>
    <version>1.3-groovy-2.4</version>
</dependency>
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy</artifactId>
    <version>2.4.17</version>
</dependency>           
  • 插件配置
在單測用例所在module的 POM檔案添加plugin的配置,多module的情況下放主pom不生效
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.22.2</version>
  <configuration>
    <skipTests>false</skipTests>
    <parallel>classes</parallel>
    <threadCount>4</threadCount>
    <forkCount>1C</forkCount>
    <includes>
      <include>**/*Test.java</include>
      <include>**/*Spec.java</include>
    </includes>
  </configuration>
</plugin>
<plugin>
  <!--groovy plugin-->
  <groupId>org.codehaus.gmavenplus</groupId>
  <artifactId>gmavenplus-plugin</artifactId>
  <version>1.4</version>
  <extensions>true</extensions>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
        <goal>testCompile</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <!-- spock單測檔案路徑 -->
    <testSources>
      <testSource>
        <directory>${project.basedir}/src/test/java</directory>
        <includes>
          <include>**/*.groovy</include>
        </includes>
      </testSource>
      <testSource>
        <directory>${project.basedir}/src/test/groovy</directory>
        <includes>
          <include>**/*.groovy</include>
        </includes>
      </testSource>
    </testSources>
  </configuration>
</plugin>           
  • 引入過程中的注意點

問題1:java.lang.NoClassDefFoundError: org/codehaus/groovy/transform/stc/AbstractExtensionMethodCache

解決:将groovy 和 groovy-all 都保持2.4.17版本

問題2:配置了gmavenplus-plugin并指定了路徑但不生效

配置在主pom中,但單測在B子產品中導緻,将插件配置到A或B子產品中

主pom

|----A

|----B

問題3:引入groovy依賴後可能會出現版本沖突的問題

因為如果你的項目引用了springboot-start-base這樣的集合式jar包,它裡面也會引用groovy,有可能跟我們引入的groovy包版本出現沖突,或者公司的一些架構也會引用groovy的包,如果版本不一緻也有可能沖突,需要排下包。

這裡推薦idea的maven插件 MavenHelper 能可視化快速排查依賴問題。

MavenHelper依賴分析

Spock核心概念

  • Spock标簽
标簽 作用 備注
given 為所測試方法做一些前置準備工作的地方 一般将方法内部依賴的參數在這個标簽内完成
when 執行測試 when 和 then 必須成對出現
then 驗證測試結果是否符合預期 when 和 then 必須成對出現
expect 精簡版when+then expect裡代碼隻可以包含布爾表達式
and 承接上一個标簽
where 用于編寫資料驅動的用例方法 總是在方法的最後,且不能重複
cleanup 測試方法退出前執行一些清理工作 一個 cleanup 後僅僅隻能跟一個where ,即使前面發生了異常cleanup也會運作(類似于Java的finally)
  • Mock、Stub和Spy差別
方法 描述
Mock() Mock的對象是一個虛拟類,為每個方法調用傳回了一個預設值,用于替換真實的類。Mock()不僅可以模拟方法傳回結果,還可以模拟方法行為,比如驗證某個方法是否被調用以及調用次數
Stub() Stub的對象也是一個虛拟類,比Mock()更簡單些。隻傳回事先準備好的測試資料,而不提供互動驗證
Spy() 又叫刺探方法,它會包裝一個真實的對象,預設情況下将調用真實的方法。Spy()功能最強大,但這樣做意味着代碼可能有壞味道,設計上可能有問題。

Spock單測示例

待測試類:

public class Square {
    private final int length;

    public Square(int length) {
        this.length = length;
    }

    public int area() {
        return length * length;
    }

    public int perimeter() {
        return length * 4;
    }
}           

測試類:

class SquareTest extends Specification {

    @Unroll
    def "given square length: #length, square area: #area, square perimeter: #perimeter"() {
        given:
        def square = new Square(
                length: length
        )
        expect:
        square.area() == area
        square.perimeter() == perimeter
        where:
        length || area | perimeter
        1      || 1    | 4
        2      || 4    | 8
    }
}           

def 是groovy的關鍵字,可以用來定義變量和方法名,這裡可以用英文也可以用中文。

expect...where... expect為核心的測試校驗語句塊,where則是多個測試用例的舉例,多個列使用"|"單豎線隔開,"||"雙豎線區分輸入和輸出變量,即左邊是輸入值,右邊是輸出值。表格列的長度不一樣,手動對齊比較麻煩,intellij idea支援format格式化快捷鍵。

Spock單元測試架構系列(二):Spock使用詳解

上面圖中定義的測試方法名使用了groovy的字面值特性,即把請求參數值和傳回結果值的字元串動态替換掉,"#length、#area、#perimeter" #号後面的變量是在方法内部定義的,前面加上#号,實作占位符的功能。

@Unroll 注解,可以把每一次調用作為一個單獨的測試用例運作,這樣運作後的單測結果更直覺:

Spock單元測試架構系列(二):Spock使用詳解

如果其中一行測試結果錯誤,spock的錯誤提示資訊也很詳細,友善我們排查(比如我們把第2行測試用例傳回的perimeter改成4)

Spock單元測試架構系列(二):Spock使用詳解

總結

在這篇文章中,我們引入了Spock,并對Spock的一些概念做了簡單介紹,最後給出一個簡單的測試示例,進一步加深對Spock的了解。下一篇章我們将會詳細的介紹如何通過Spock測試複雜的if-else場景。

繼續閱讀