如何簡單的使用Groovy+Spock寫單測用例

概述

  軟件測試是軟件開發中必不可少的流程之一,但是軟件測試又全部都是測試人員的工作,作爲開發人員最好也承擔其中的一部分工作,因爲開發人員瞭解自己的功能需要覆蓋哪些必要的場景,而測試人員是幫你找到你沒有覆蓋到的場景。而且寫單測用例能夠有效的幫助項目做CI與DI。所以,既然是一件不可避免的事,我們何不讓其變得簡單呢。

依賴與基礎

本人的項目環境如下:
JDK8,Spring Boot 2.2.0.RELEASE
要使用Groovy+Spock編寫單測,首先引入如下Maven依賴,同時安裝Groovy插件。

<dependency>
     <groupId>org.spockframework</groupId>
     <artifactId>spock-core</artifactId>
     <version>1.2-groovy-2.4</version>
     <scope>test</scope>
 </dependency>

 <dependency>
     <groupId>org.spockframework</groupId>
     <artifactId>spock-spring</artifactId>
     <version>1.2-groovy-2.4</version>
     <scope>test</scope>
 </dependency>

接下來我們來講一下Groovy的常見語法,作爲一門動態語言,它的寫法比Java簡單很多,而且基本只要你會寫Java,你很容易就可以上手Groovy。
首先看一下測試模板方法的定義和JUnit的對比:
在這裏插入圖片描述
Spock的模板方法說明:

def setupSpec() {}    // runs once -  before the first feature method
def setup() {}        // runs before every feature method
def cleanup() {}      // runs after every feature method
def cleanupSpec() {}  // runs once -  after the last feature method

常見用法

expect-where

有時,裏面的某個測試用例失敗了,卻難以查到是哪個失敗了。這時候,可以使用Unroll註解,該註解會將where子句的每個測試用例轉化爲一個 @Test 獨立測試方法來執行,這樣就很容易找到錯誤的用例。 方法名還可以更可讀些。

@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class StaffQueryServiceSpec extends Specification {
	@Autowired
	private StaffQueryService staffQueryService;
	
	@Unroll
	def "通過歸屬人ID查詢員工"(){
	expect:
	staffQueryService.getStaffById(ownerId)!=null
	where:
	ownerId | _
	1 | _
	2 | _
	}
}

where後面的條件可以通過表格的形式或者數據管道的形式,單個數據的時候可以通過“_”構建表格

def "通過歸屬人ID查詢員工"(){
expect:
staffQueryService.getStaffById(ownerId)!=null
where:
ownerId << [1,2,3]
}
given-expect-where
@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class OrderQueryServiceSpec extends Specification {
	@Autowired
	private OrderQueryService orderQueryService;
	
@Unroll
 def "根據經銷商機構判斷是否存在在途訂單"() {
	  given:
	  def orgId = 1
	  def dealerId = dealer
	  expect:
	  orderQueryService.hasOnWayOrder(dealerId, orgId) == result
	  where:
	  dealer | result
	  1        | true
	  9999     | false
 }
} 
given-when-then

這個比較符合敏捷開發中用戶故事的描述,也是用的最多的一種寫法。

@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class OrderAppServiceSpec extends Specification {
	@Autowired
	private OrderAppService orderAppService;
	
def "取消訂單"() {
        given:
        def dto = new CancelOrderDTO(operatorId: "1", operatorName: "tzx", dealerId: 1)
        when:
        dto.setOrderNo("OR202001020014")
        orderAppService.cancelOrder(dto)
        then:
        noExceptionThrown()
        when:
        dto.setOrderNo("OR202001020010")
        orderAppService.cancelOrder(dto)
        then:
        thrown(Exception)
    }
}    
given-when-then-where
@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class OrderAppServiceSpec extends Specification {
	@Autowired
	private OrderAppService orderAppService;
	
def "取消訂單"() {
        given:
        def dto = new CancelOrderDTO(operatorId: operatorId, operatorName: operatorName, dealerId: dealerId)
        when:
        dto.setOrderNo("OR202001020014")
        orderAppService.cancelOrder(dto)
        then:
        noExceptionThrown()
        where:
        operatorId| operatorName | dealerId
        1| "tzs" | 1
        2| "zhangsan" | 1
        3 | "wangwu" | 2
    }
}    

如果有多個條件需要測試時可以採用這種寫法

given-when-then-thrown

在 when 子句中調用了會拋出異常的方法,而在 then 子句中,使用 thrown 接收方法拋出的異常,並賦給指定的變量 ex, 之後就可以對 ex 進行斷言了。

@SpringBootTest(classes = BootstrapApplication)
@Transactional
@Rollback
class CustomerAppServiceSpec extends Specification {
	@Autowired
	private CustomerAppService customerAppService;
	
	def "更新未存在客戶" (){
	 given:
	 def dto = new CustomerCreateDTO(customerId: -1,customerName: "小王");
	 when: "客戶不存在時"
	 customerAppService.updateCustomer(dto);
	 then:
	 def ex = thrown(ConnectorBusinessException)
	 ex.class.name = "java.lang.RuntimeException"
	 ex.cause.class.name = "com.souche.connector.common.exception.ConnectorBusinessException"
	}
}
打樁

很多時候我們測試的時候不需要執行真正的方法,因爲在測試代碼裏面構建一個真實的對象是比較麻煩的,特別是使用一些依賴注入框架的時候,因此有了打樁的功能。
比如我們依賴某個外部應用的service的返回值,可以採用如下的方式。

@SpringBean
 UserCenterQueryService userCenterQueryService = Mock{
     getUserByShopCodeAndRole(_,_) >> "001"
 };

如果需要定義多次調用返回不同的返回值可以採用下面的方式

@SpringBean
UserCenterQueryService userCenterQueryService = Mock{
    getUserByShopCodeAndRole(_,_)  >>>  ["001","002"]
};

總結

本文介紹了expect-where,given-expect-where,given-when-then,given-when-then-where
given-when-then-thrown,打樁等用法,相信已經適用了絕大多數場景。

參考鏈接

官方文檔
GitHub連接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章