Java中Spock框架Mock對象的方法經(jīng)驗總結(jié)
前言:
下面分享一些使用過的一個常用項目,部分信息隱去了。大家在自己項目中實踐的時候可以參考,盡量別直接抄代碼,我自己使用過程中有很多兼容性的坑,特別是IDE自動import
功能。
一、技術(shù)方案
本技術(shù)方案基于公司力推的Spock
單元測試框架,spock
是一款基于Groovy語言的單元測試框架,其基礎(chǔ)也是Java的Junit,目前最新版已經(jīng)到了2.0,但對Groovy和相應的Java版本要求較高,所以Groovy版本使用1.+,Spock自帶的Mock和Spy足夠好了,對于對象行為的模擬滿足絕大部分場景,但是涉及靜態(tài)方法模擬時候存在局限性,所以引入Mockito
和PowerMock
來實現(xiàn)設計靜態(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>
因為Spock
本身需要Groovy
語言支持,所以也需要一個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>
另外,提供的配置文件中多了幾項特殊場景下使用的依賴,提供參考:
? <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)資源
由于多個單測框架的方法名重復較多,我把import
內(nèi)容也貼出來了,如果同樣的代碼無法運行,可以排查一下是否import
正確的方法和類。這里不是很建議import static
,因為可能出現(xiàn)混用以及不易排查的問題。
由于目前測試中沒有遇到使用Spy放行的邏輯,所以均使用Mock模式,需要對Mock對象的方法進行模擬。這個分為兩類:Spock和PowerMock
(結(jié)合Mockito)。原因是在混合靜態(tài)資源和非靜態(tài)資源場景下,指定了PowerMock
的@RunWith
運行規(guī)則,不兼容Spock寫法,需要用到PowerMock
框架Mock對象的功能。
三、Mock被測對象
1.@Autowired構(gòu)造方法
用一個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注解,服務啟動會報錯。
源代碼如下:
@Component @Slf4j public class TaskScheduled { ? ? @Autowired ? ? IService service; ? ?? ? ? @Value("${hickwall.statistic.cid}") ? ? public ?String cid; }
4.共享對象以及初始化
統(tǒng)一使用Spock提供的功能,用到的注解@Shared,不加的話無法在Spock方法中進行賦值操作,但是可以當做一個普通的對象使用。
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方法的時候?qū)?shù)進行了斷言和處理,這也是Spock框架的一個特性,其他均為Groovy
語法特性。
其他定義Mock行為的語法如下:
? service.getAllGroup(_,_) >> null//返回null ? ? ? ? service.getAllGroup(_,_) >> {throw new Exception()} //拋出異常 ? ? ? ? service.getAllGroup(_,_) >> []//返回空list,Groovy默認實現(xiàn)ArrayList ? ? ? ? service.getAllGroup(_,_) >> [slaBean,slaBean]//返回正常list ? ? ? ? service.getAllGroup(_,_) >> [slaBean,slaBean]//返回正常list ? ? ? ? service.getAllGroup(_,10) >> [slaBean,slaBean]//定時某個參數(shù) ? ? ? ? service.getAllGroup(any(),10) >> [slaBean,slaBean]//any()等效于_ ? ? ? ? service.getAllGroup(any(),10) >> service.getAllGroup(1,10)//調(diào)用其他方法返回
2.Mockito模擬對象行為
Mockito
和PowerMock
配合使用語法稍微復雜一些。首先我們需要先定義對象行為(通常在com.funtesterbase.task.TaskScheduledTest#setupSpec
方法中),然后在用例用使用。
定時對象行為:
? ? ? ? Mockito.when(newutil.filter(Mockito.any())).thenReturn(true)
定義行為以后,就可以在Spock用例中正常使用,包括在通過Mock對象創(chuàng)建的對象方法中,如果調(diào)用到定義過行為的方法,也會走自定義的邏輯。
其他常用定義行為:
? ? ? ? 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,什么都不做
第三個例子中我們假設filter
方法是一個無返回的void
方法。
通常我們需要構(gòu)建返回對象,如果對象需要賦值的屬性過多,可以使用初始化賦值的方法,下面是Mock一個返回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)驗總結(jié)的文章就介紹到這了,更多相關(guān)Spock框架Mock對象內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基于堆結(jié)構(gòu)實現(xiàn)優(yōu)先隊列功能示例
這篇文章主要介紹了Java基于堆結(jié)構(gòu)實現(xiàn)優(yōu)先隊列功能,結(jié)合實例形式分析了java優(yōu)先隊列的簡單定義與使用方法,需要的朋友可以參考下2017-11-11