一、導入依賴
Spock是基於JUnit的單測框架,提供一些更好的語法,結合Groovy語言,可以寫出更為簡潔的單測。
<!-- groovy依賴 -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.0</version>
</dependency>
<!-- spock核心依賴 -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.3-groovy-2.4</version>
<scope>test</scope>
</dependency>
<!-- spring spock依賴 -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.3-groovy-2.4</version>
<scope>test</scope>
</dependency>
<!-- 單元測試依賴 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
二、測試例子
繼承Specification
類
package com.qiang.groovy.controller
import com.qiang.groovy.controller.ConfigInfoController
import spock.lang.Specification
class ConfigInfoControllerGroovy extends Specification {
}
固定方法
/**
* 在第一個測試方法開始前執行一遍
*/
def setupSpec() {
println "------------ setupSpec()方法 ------------"
}
/**
* 每個測試方法開始前都會執行一遍
*/
def setup() {
println "------------ setup()方法 ------------"
}
/**
* 每個測試方法后都會執行一遍
*/
def cleanup() {
println "------------ cleanup()方法 ------------"
}
/**
* 最后一個測試方法后執行
*/
def cleanupSpec() {
println "------------ cleanupSpec()方法 ------------"
}
測試例子
def "測試a>b"() {
given:
def a = new Random().nextInt(10)
def b = 2
expect:
println a
a > b
}
點擊運行
測試通過
測試不通過
三、基本構造
- where: 以表格的形式提供測試數據集合
- when: 觸發行為,比如調用指定方法或函數
- then: 做出斷言表達式
- expect: 期望的行為,when-then的精簡版
- given: mock單測中指定mock數據
- thrown: 如果在when方法中拋出了異常,則在這個子句中會捕獲到異常並返回
- def setup() {} :每個測試運行前的啟動方法
- def cleanup() {} : 每個測試運行后的清理方法
- def setupSpec() {} : 第一個測試運行前的啟動方法
- def cleanupSpec() {} : 最后一個測試運行后的清理方法
四、構造例子
4.1 expect-where
在where子句中以表格形式給出一系列輸入輸出的值,然后在expect中引用。
@Unroll
def "測試expect-where"() {
expect:
userInfoService.getById(id).getName() == name
where:
id | name
1 | "小強"
2 | "傻狗"
3 | "小豬"
}
4.2 given-when-then
當條件滿足時,達到期望的結果。
@Unroll
@Transactional
def "測試given-when-then"() {
given:
// 數據准備
UserInfo userInfo = new UserInfo()
userInfo.setName("小強崽")
userInfo.setAsset(10000000.00)
when:
// 條件判斷
boolean flag = userInfoService.save(userInfo)
then:
// 期望結果
flag
}
4.3 when-then-thrown
測試異常信息。
/**
* 模擬異常
*/
@Override
public void getExceptionMessage() {
throw new RuntimeException("模擬異常");
}
測試方法。
def "測試異常thrown"() {
when:
// 此方法會拋出RuntimeException
userInfoService.getExceptionMessage()
then:
// 接收異常
def ex = thrown(Exception)
ex.class.name == "java.lang.RuntimeException"
ex.getMessage() == "模擬異常"
}
五、遠程擋板
遠程調用第三方服務需要Mock擋板,避免受第三方服務的影響。
遠程調用服務
package com.qiang.groovy.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qiang.common.response.ResponseResult;
import com.qiang.groovy.entity.UserInfo;
import com.qiang.groovy.feign.GiteeServiceFeign;
import com.qiang.groovy.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.Serializable;
import java.util.List;
/**
* @author 小強崽
* @create: 2021-04-11 21:31:14
* @description: 控制層
*/
@RestController
@RequestMapping("/user/info")
public class UserInfoController {
/**
* 注入遠程調用接口
*/
@Autowired
private GiteeServiceFeign giteeServiceFeign;
/**
* 遠程調用
*
* @param msg
* @return
*/
@GetMapping("/gitee/test/feign")
public ResponseResult<String> testFeign(@RequestParam("msg") String msg) {
return giteeServiceFeign.testFeign(msg);
}
}
遠程調用做擋板后,自定義擋板返回的參數即可。
package com.qiang.groovy.controller
import com.qiang.common.response.ResponseResult
import com.qiang.common.util.SpringContextUtil
import com.qiang.groovy.feign.GiteeServiceFeign
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification
@SpringBootTest
class UserInfoControllerGroovy extends Specification {
@Autowired
private UserInfoController userInfoController
@Autowired
private SpringContextUtil springContextUtil;
/**
* 單元測試調用第三方服務時,需要做擋板
*/
def giteeServiceFeign = Mock(GiteeServiceFeign)
def "測試遠程調用"() {
given:
// 定義擋板返回的參數,(*_)為任意入參參數
giteeServiceFeign.testFeign(*_) >> ResponseResult.success()
expect:
userInfoController.testFeign(msg).code == result
where:
msg | result
"msg" | "200"
}
/**
* 每個測試方法開始前都會執行一遍
*/
def setup() {
// 擋板賦值
userInfoController.giteeServiceFeign = giteeServiceFeign
}
/**
* 每個測試方法后都會執行一遍
*/
def cleanup() {
// 還原擋板
userInfoController.giteeServiceFeign = springContextUtil.getBean(GiteeServiceFeign.class)
}
}
六、常用注解
6.1 @Unroll
某個測試用例失敗了,卻難以查到是哪個失敗了,這時候,可以使用@Unroll注解,該注解會將where子句的每個測試用例轉化為一個 @Test 獨立測試方法來執行,這樣就很容易找到錯誤的用例。
@Unroll
def "測試getById()"() {
expect:
userInfoService.getById(id).getName() == name
where:
id | name
1 | "小強"
2 | "傻狗"
3 | "小豬"
}
沒加@Unroll之前
加了@Unroll之后
6.2 @Transactional
插入數據時,加上該注解測試完成會回滾數據。
作者(Author):小強崽
來源(Source):https://www.wuduoqiang.com/archives/Groovy+Spock單元測試
協議(License):署名-非商業性使用-相同方式共享 4.0 國際 (CC BY-NC-SA 4.0)
版權(Copyright):商業轉載請聯系作者獲得授權,非商業轉載請注明出處。 For commercial use, please contact the author for authorization. For non-commercial use, please indicate the source.