Java中Spock框架Mock對象的方法經(jīng)驗(yàn)總結(jié)
前言:
下面分享一些使用過的一個(gè)常用項(xiàng)目,部分信息隱去了。大家在自己項(xiàng)目中實(shí)踐的時(shí)候可以參考,盡量別直接抄代碼,我自己使用過程中有很多兼容性的坑,特別是IDE自動(dòng)import功能。
一、技術(shù)方案
本技術(shù)方案基于公司力推的Spock單元測試框架,spock是一款基于Groovy語言的單元測試框架,其基礎(chǔ)也是Java的Junit,目前最新版已經(jīng)到了2.0,但對Groovy和相應(yīng)的Java版本要求較高,所以Groovy版本使用1.+,Spock自帶的Mock和Spy足夠好了,對于對象行為的模擬滿足絕大部分場景,但是涉及靜態(tài)方法模擬時(shí)候存在局限性,所以引入Mockito和PowerMock來實(shí)現(xiàn)設(shè)計(jì)靜態(tài)方法的測試模擬場景。
以下為相關(guān)依賴版本:
? <dependency> ? ? ? ? ? ? <groupId>org.spockframework</groupId> ? ? ? ? ? ? <artifactId>spock-core</artifactId> ? ? ? ? ? ? <version>1.2-groovy-2.5</version> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.spockframework</groupId> ? ? ? ? ? ? <artifactId>spock-spring</artifactId> ? ? ? ? ? ? <version>1.2-groovy-2.5</version> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.powermock</groupId> ? ? ? ? ? ? <artifactId>powermock-api-mockito2</artifactId> ? ? ? ? ? ? <version>1.7.4</version> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.powermock</groupId> ? ? ? ? ? ? <artifactId>powermock-module-junit4</artifactId> ? ? ? ? ? ? <version>1.7.4</version> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.mockito</groupId> ? ? ? ? ? ? <artifactId>mockito-core</artifactId> ? ? ? ? ? ? <version>2.8.9</version> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? </dependency>
因?yàn)?code>Spock本身需要Groovy語言支持,所以也需要一個(gè)Groovy-all的依賴,請注意注自帶的注釋:
? ? ? <dependency> <!-- use a specific Groovy version rather than the one specified by spock-core --> ? ? ? ? ? ? <groupId>org.codehaus.groovy</groupId> ? ? ? ? ? ? <artifactId>groovy-all</artifactId> ? ? ? ? ? ? <version>2.4.7</version> ? ? ? ? </dependency>
另外,提供的配置文件中多了幾項(xiàng)特殊場景下使用的依賴,提供參考:
? <dependency> <!-- enables mocking of classes (in addition to interfaces) --> ? ? ? ? ? ? <groupId>net.bytebuddy</groupId> ? ? ? ? ? ? <artifactId>byte-buddy</artifactId> ? ? ? ? ? ? <version>1.9.9</version> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? </dependency> ? ? ? ? <dependency> <!-- enables mocking of classes without default constructor (together with CGLIB) --> ? ? ? ? ? ? <groupId>org.objenesis</groupId> ? ? ? ? ? ? <artifactId>objenesis</artifactId> ? ? ? ? ? ? <version>3.0.1</version> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? </dependency> ? ? ? ? <dependency> <!-- only required if Hamcrest matchers are used --> ? ? ? ? ? ? <groupId>org.hamcrest</groupId> ? ? ? ? ? ? <artifactId>hamcrest-core</artifactId> ? ? ? ? ? ? <version>2.1</version> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? </dependency>
二、非靜態(tài)資源
由于多個(gè)單測框架的方法名重復(fù)較多,我把import內(nèi)容也貼出來了,如果同樣的代碼無法運(yùn)行,可以排查一下是否import正確的方法和類。這里不是很建議import static ,因?yàn)榭赡艹霈F(xiàn)混用以及不易排查的問題。
由于目前測試中沒有遇到使用Spy放行的邏輯,所以均使用Mock模式,需要對Mock對象的方法進(jìn)行模擬。這個(gè)分為兩類:Spock和PowerMock(結(jié)合Mockito)。原因是在混合靜態(tài)資源和非靜態(tài)資源場景下,指定了PowerMock的@RunWith運(yùn)行規(guī)則,不兼容Spock寫法,需要用到PowerMock框架Mock對象的功能。
三、Mock被測對象
1.@Autowired構(gòu)造方法
用一個(gè)controller舉例,源代碼如下:
@Api(tags = "SLA規(guī)則管理模塊")
@Slf4j
@RestController
@RequestMapping("/hickwall/v1/static/sla")
public class FunController {
? ? HttpServletRequest request;
? ? ISlaService service;
? ? @Autowired
? ? public FunController(HttpServletRequest request, ISlaService service) {
? ? ? ? this.request = request;
? ? ? ? this.service = service;
? ? }
}Spock單測代碼如下:
import com.funtester.service.ISlaService
import com.funtester.vo.sla.SlaBean
import spock.lang.Shared
import spock.lang.Specification
import javax.servlet.http.HttpServletRequest
class FunControllerTest extends Specification {
? ? def service = Mock(ISlaService)
? ? @Shared
? ? def request = Mock(HttpServletRequest)
? ??
? ? def FunController = new FunController(request, service)
}2.@Autowired屬性對象,無構(gòu)造方法
源代碼如下:
public class ApiImpl implements IApi {
? ? @Autowired
? ? private ApiRMapper mapper;
}Spock單測部分代碼如下:
import com.funtester.mapper.ApiRMapper import com.funtester.vo.ApiR import spock.lang.Shared import spock.lang.Specification ? ? ApiRMapper mapper = Mock(ApiRMapper) ? ? def drive = new ApiImpl(mapper:mapper)
3.PowerMock用法
場景也分為兩種:有無構(gòu)造方法,除了Mock方法不同以外,其他均相同,這里不列舉。
PS:如果對象屬性中有未被@Autowired注釋的屬性,不能用@AllArgsConstructor的lombok注解,服務(wù)啟動(dòng)會(huì)報(bào)錯(cuò)。
源代碼如下:
@Component
@Slf4j
public class TaskScheduled {
? ? @Autowired
? ? IService service;
? ??
? ? @Value("${hickwall.statistic.cid}")
? ? public ?String cid;
}4.共享對象以及初始化
統(tǒng)一使用Spock提供的功能,用到的注解@Shared,不加的話無法在Spock方法中進(jìn)行賦值操作,但是可以當(dāng)做一個(gè)普通的對象使用。
Spock框架Demo:
? ? @Shared
? ? def slaBean = new SlaBean()
? ? def setupSpec() {
? ? ? ? request.getHeader("operator") >> "FunTester"
? ? ? ? slaBean.name = "測試"
? ? ? ? slaBean.quota = 1
? ? ? ? slaBean.upstream = "PRO"
? ? ? ? slaBean.threshold = 0.1
? ? ? ? slaBean.creator = "FunTester"
? ? ? ? slaBean.id = 100
? ? }四、定義對象行為
1.Spock定義Mock對象行為
基礎(chǔ)的Spock語法結(jié)構(gòu)when-then-expct如下:
? ? def "AddSla"() {
? ? ? ? when:
? ? ? ? def sla = FunController.addSla(slaBean)
? ? ? ? then:
? ? ? ? service.addSla(_) >> {f ->
? ? ? ? ? ? assert "FunTester" in f.creator
? ? ? ? ? ? 1
? ? ? ? }
? ? ? ? expect:
? ? ? ? sla.code == 0
? ? ? ? sla.data == 1
? ? }也可以在最開始加上given,when和then通常一起使用。
上述Demo在Mock方法的時(shí)候?qū)?shù)進(jìn)行了斷言和處理,這也是Spock框架的一個(gè)特性,其他均為Groovy語法特性。
其他定義Mock行為的語法如下:
? service.getAllGroup(_,_) >> null//返回null
? ? ? ? service.getAllGroup(_,_) >> {throw new Exception()} //拋出異常
? ? ? ? service.getAllGroup(_,_) >> []//返回空list,Groovy默認(rèn)實(shí)現(xiàn)ArrayList
? ? ? ? service.getAllGroup(_,_) >> [slaBean,slaBean]//返回正常list
? ? ? ? service.getAllGroup(_,_) >> [slaBean,slaBean]//返回正常list
? ? ? ? service.getAllGroup(_,10) >> [slaBean,slaBean]//定時(shí)某個(gè)參數(shù)
? ? ? ? service.getAllGroup(any(),10) >> [slaBean,slaBean]//any()等效于_
? ? ? ? service.getAllGroup(any(),10) >> service.getAllGroup(1,10)//調(diào)用其他方法返回2.Mockito模擬對象行為
Mockito和PowerMock配合使用語法稍微復(fù)雜一些。首先我們需要先定義對象行為(通常在com.funtesterbase.task.TaskScheduledTest#setupSpec方法中),然后在用例用使用。
定時(shí)對象行為:
? ? ? ? Mockito.when(newutil.filter(Mockito.any())).thenReturn(true)
定義行為以后,就可以在Spock用例中正常使用,包括在通過Mock對象創(chuàng)建的對象方法中,如果調(diào)用到定義過行為的方法,也會(huì)走自定義的邏輯。
其他常用定義行為:
? ? ? ? Mockito.when(newutil.filter(Mockito.any())).thenReturn(null) ? ? ? ? Mockito.when(newutil.filter(Mockito.any())).thenThrow(Exception.class)//拋出異常 ? ? ? ? PowerMockito.doNothing().when(newutil).filter(Mockito.any(ArrayList.class))//dothing,什么都不做
第三個(gè)例子中我們假設(shè)filter方法是一個(gè)無返回的void方法。
通常我們需要構(gòu)建返回對象,如果對象需要賦值的屬性過多,可以使用初始化賦值的方法,下面是Mock一個(gè)返回list的方法返回值的Demo:
Mockito.when(newser.selectAllService()).thenReturn([new NewInterface() {
? ? ? ? ? ? {
? ? ? ? ? ? ? ? setUrl("/abc")
? ? ? ? ? ? ? ? setNname("test TGFC z;/./")
? ? ? ? ? ? ? ? setMethod("GET")
? ? ? ? ? ? }
? ? ? ? }, new NewInterface() {
? ? ? ? ? ? {
? ? ? ? ? ? ? ? setUrl("/abcd")
? ? ? ? ? ? ? ? setNname("test")
? ? ? ? ? ? ? ? setMethod("POST")
? ? ? ? ? ? }
? ? ? ? }, new NewInterface() {
? ? ? ? ? ? {
? ? ? ? ? ? ? ? setUrl("/abce")
? ? ? ? ? ? ? ? setNname("test")
? ? ? ? ? ? ? ? setMethod("GET")
? ? ? ? ? ? }
? ? ? ? }])到此這篇關(guān)于Spock框架Mock對象的方法經(jīng)驗(yàn)總結(jié)的文章就介紹到這了,更多相關(guān)Spock框架Mock對象內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA 自定義方法注解模板的實(shí)現(xiàn)方法
這篇文章主要介紹了IDEA 自定義方法注解模板的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Java基于堆結(jié)構(gòu)實(shí)現(xiàn)優(yōu)先隊(duì)列功能示例
這篇文章主要介紹了Java基于堆結(jié)構(gòu)實(shí)現(xiàn)優(yōu)先隊(duì)列功能,結(jié)合實(shí)例形式分析了java優(yōu)先隊(duì)列的簡單定義與使用方法,需要的朋友可以參考下2017-11-11

