Spock 簡易使用說明
使用 Spock 來開發(fā)測試程式除了在之前的文章中提到的:可輔助 BDD 的開發(fā)流程楞艾、與 JUnit 相容及內(nèi)建 Data Driven 功能等特性之外篓冲,還有另外一項(xiàng)優(yōu)點(diǎn)是內(nèi)建了 Mock 的功能寒锚。Spock 所提供的 Mock 功能在使用上相當(dāng)?shù)暮啙崳韵率且欢畏独脑创a:
def "Spock 范例測試程式"() {
given:
def mockClass = Mock(ConcreteClass)
def mockInterface1 = Mock(IInjection)
IInjection mockInterface2 = Mock()
Processor tester = new Processor(mockClass)
mockInterface2.getInfo() >> { "二號測試字串" }
when:
tester.run(mockInterface1)
then:
1 * mockClass.start()
1 * mockInterface1.getInfo() >> { "一號測試字串" }
_ * mockClass.process(_) >> { mockInterface2 }
1 * mockInterface2.setData(_)
1 * mockClass.end()
}
由以上的源代碼可以看到只要使用 Mock() 方法就可以對指定的對象進(jìn)行 Mock 的程序乓旗,同時也提供二種彈性的聲明方式府蛇,一是把要 Mock 的對象傳入 Mock() 方法(第 3, 4 行),另一個方式是使用 Mock() 方法來建立實(shí)例(第 5 行)寸齐。第二種 Java-like 的語法主要是希望在編輯源代碼時 IDE 可以提供較好的支持欲诺,運(yùn)行時會以聲明的型別來判定要產(chǎn)生的 Mock 對象之 Instance。
Spock 所提供的 Mock 功能有另一個亮點(diǎn)是不僅能夠針對 Interface渺鹦,同時也可以使用在一般的 Class 上扰法。如此在進(jìn)行系統(tǒng)設(shè)計時,就可以不用遷就 “是否有辦法分開測試” 這個問題而要使用大量的 IoC 在設(shè)計之中毅厚,以致形成了過度設(shè)計的情況塞颁。在 Mock Class 前,項(xiàng)目必須要引用 cglib-nodep 和 objenesis 這二個庫吸耿,所以在 build.gradle 的 dependencies 應(yīng)該像以下所示范的內(nèi)容:
dependencies {
testCompile 'org.codehaus.groovy:groovy-all:2.4.4'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'org.objenesis:objenesis:2.2'
testCompile 'cglib:cglib-nodep:3.1'
}
根據(jù)官方文件的說明祠锣,如果要驗(yàn)證目標(biāo) Class 與其周邊 Object 互動的過程是否符合像是序列圖所表達(dá)的設(shè)計內(nèi)容,可以使用范例程式中所示范之內(nèi)容咽安。使用的語法規(guī)格是:[互動的次數(shù)] * [互動的標(biāo)的]伴网,1 * mockClass.start() 就是驗(yàn)證 Processor.run 是否有調(diào)用 ConcreteClass.start(),而且整個 Processor.run 的運(yùn)行過程中只調(diào)用一次妆棒。
從范例程式中還可以看到澡腾,Spock 所提供的 Mock 功能,除了可以用來驗(yàn)證 Class 間互動過程糕珊,同時也具有 Stub 的能力动分。可以依據(jù)調(diào)用的內(nèi)容來提供對應(yīng)的動作红选,增加了測試程式的靈活性澜公。像范例程式中的第 8 行,當(dāng)有程式調(diào)用 mockInterface2.getInfo() 時喇肋,就會固定傳回 "二號測試字串" 的內(nèi)容坟乾。而在第 15, 16 行迹辐,是代表當(dāng) Processor.run 的運(yùn)行過程中,有調(diào)用到這二個目標(biāo)函式時所會取得的內(nèi)容甚侣。
如何 Mock 靜態(tài)方法
雖然 Spock 提供了許多優(yōu)異的特性右核,不過,美中不足的地方是目前的版本沒有辦法 Mock 靜態(tài)方法渺绒。在網(wǎng)路上看了一些討論之后,PowerMock 似乎是一個可用來搭配 Spock 解決這個問題的框架菱鸥。進(jìn)一步了解發(fā)現(xiàn) PowerMock 所提供的套件種類繁多宗兼,包含了與幾個主流的測試框架整合的功能。而如何在 Gradle 中設(shè)定對應(yīng)的套件氮采,可以讓 Spock 與 PowerMock 協(xié)同運(yùn)作會是一個門檻殷绍。
基本上要在 Spock 中使用 PowerMock 來 Mock 靜態(tài)方法,會用到 JUnit鹊漠、Mockito 和 PowerMock 這幾個框架主到,這使得在設(shè)定 build.gradle 內(nèi)容時更加的棘手。而在測試程式中要把這四個框架整合起來躯概,以便能夠達(dá)到 Mock 靜態(tài)方法的目的登钥,也是很費(fèi)心神的一件事。
目前網(wǎng)絡(luò)上查到的資訊都是片斷的娶靡,所以在這里把相關(guān)的資訊統(tǒng)整起來牧牢,做為日后研究的起點(diǎn)。以下是 build.gradle 的內(nèi)容:
apply plugin: 'java'
apply plugin: 'groovy'
dependencies {
testCompile 'org.codehaus.groovy:groovy-all:2.4.4'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'org.objenesis:objenesis:2.2'
testCompile 'cglib:cglib-nodep:3.1'
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'org.powermock:powermock-api-mockito:1.6.2'
testCompile 'org.powermock:powermock-module-junit4:1.6.2'
testCompile 'org.powermock:powermock-module-junit4-rule:1.6.2'
testCompile 'org.powermock:powermock-classloading-xstream:1.6.2'
}
以上的套件應(yīng)該要依照所屬框架更新的狀況來調(diào)整對應(yīng)的版本編號姿锭。
在測試程式中塔鳍,如果有一個以下待測的 Class:
public class TestClass {
public static String staticMethod() {
return null;
}
}
測試程式應(yīng)該像以下所示范的內(nèi)容來進(jìn)行靜態(tài)方法的測試:
import org.junit.Rule
import org.mockito.Mockito
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.rule.PowerMockRule
import spock.lang.Specification
@PrepareForTest([TestClass.class])
class MockStaticMethodSpec extends Specification {
@Rule
PowerMockRule mPowerMockRule = new PowerMockRule();
def "測試靜態(tài)方法"() {
setup :
PowerMockito.mockStatic(TestClass.class)
when :
Mockito.when(TestClass.staticMethod()).thenReturn("測試用字串")
then :
TestClass.staticMethod() == "測試用字串"
}
}