by(uri("/openptk-server/login")),
by("clientid=test_app&clientcred=fake_password"))).response(status(200));
接下來(lái)我們告訴要測試的網(wǎng)絡(luò )端點(diǎn),應該訪(fǎng)問(wèn)位于localhost:12306的服務(wù)器,并提供用戶(hù)名和密碼:
configuration = new IdentityServiceConfiguration();
configuration.setHost("http://localhost:12306");
configuration.setClientId("test_app");
configuration.setClientCredential("fake_password");
xmlEndPoint = new XmlEndPoint(configuration);
然后就可以正式開(kāi)始測試了。首先我們測試XmlEndPoint可以用GET方法訪(fǎng)問(wèn)一個(gè)指定的URL,取回應答正文:
@Test
public void shouldBeAbleToCarryGetRequest() throws Exception {
final String expectedResponse = "SUCCESS";
server.get(by(uri("/get_path"))).response(expectedResponse);
running(server, new Runnable() {
@Override
public void run() {
XmlEndPointResponse response =
xmlEndPoint.get("http://localhost:12306/get_path");
assertThat(response.getStatusCode(), equalTo(STATUS_SUCCESS));
assertThat(response.getResponseBody(), equalTo(expectedResponse));
}
});
}
實(shí)現了這個(gè)測試以后,我們再添加一個(gè)測試,描述“應用程序登錄失敗”的場(chǎng)景,這樣我們就得到了對XmlEndPoint類(lèi)的get方法的完全測試覆蓋:
@Test(expected = IdentityServiceSystemException.class)
public void shouldRaiseExceptionIfLoginFails() throws Exception {
configuration.setClientCredential("wrong_password");
running(server, new Runnable() {
@Override
public void run() {
xmlEndPoint.get("http://localhost:12306/get_path");
}
});
}
以此類(lèi)推,也很容易給post和put方法添加測試。于是,在Moco的幫助下,我們就完成了對網(wǎng)絡(luò )端點(diǎn)的測試。雖然這部分測試真的發(fā)起了HTTP 請求,但只是針對位于localhost的Moco服務(wù)器,并且測試的內容也只是最基本的GET/POST/PUT請求,因此測試仍然快且穩定。
Moco的前世今生
在ThoughtWorks成都分公司,我們?yōu)橐患冶kU企業(yè)開(kāi)發(fā)在線(xiàn)應用。由于該企業(yè)的數據與核心保險業(yè)務(wù)邏輯存在于COBOL開(kāi)發(fā)的后端系統中,我們所開(kāi)發(fā)的在線(xiàn)應用都有大量集成工作。不止一個(gè)項目組發(fā)出這樣的抱怨:因為依賴(lài)了被集成的遠程服務(wù),我們的測試變得緩慢而不穩定。于是,我們的一位同事鄭曄[4]開(kāi)發(fā)了Moco框架,用它來(lái)簡(jiǎn)化集成點(diǎn)的測試。
除了我們已經(jīng)看到的API模式(在測試用例中使用Moco提供的API)以外,Moco還支持standalone模式,用于快速創(chuàng )建一個(gè)測試用的服務(wù)器。例如下列配置(位于名為“foo.json”的文件中)就描述了一個(gè)最基本的HTTP服務(wù)器:
[
{
"response" : {
"text" : "Hello, Moco"
}
}
]
把這個(gè)服務(wù)器運行起來(lái):
java -jar moco-runner--standalone.jar -p 12306 foo.json
再訪(fǎng)問(wèn)“http://localhost:12306”下面的任意URL,都會(huì )看到“Hello, Moco”的字樣。結合各種靈活的配置,我們就可以很快地模擬出需要被集成的遠程服務(wù),用于本地的開(kāi)發(fā)與功能測試。
感謝開(kāi)源社區的力量,來(lái)自澳大利亞的Garrett Heel給Moco開(kāi)發(fā)了一個(gè)Maven插件[5],讓我們可以在構建過(guò)程中適時(shí)地打開(kāi)和關(guān)閉Moco服務(wù)器(例如在運行Cucumber[6]功能測試之前啟動(dòng)Moco服務(wù)器,運行完功能測試之后關(guān)閉),從而更好地把Moco結合到構建過(guò)程中。
目前Moco已經(jīng)被ThoughtWorks成都分公司的幾個(gè)項目使用,并且根據這些項目提出的需求繼續演進(jìn)。如果你有興趣參與這個(gè)開(kāi)源項目,不論是使用它并給它提出改進(jìn)建議,還是為它貢獻代碼,鄭曄都會(huì )非常開(kāi)心。
其它組件的測試
有了針對網(wǎng)絡(luò )端點(diǎn)的測試之后,其他幾個(gè)組件的測試已經(jīng)可以不必發(fā)起網(wǎng)絡(luò )請求。理論上來(lái)說(shuō),每個(gè)組件都應該獨自隔離進(jìn)行單元測試;但個(gè)人而言,對于沒(méi)有外部依賴(lài)的對象,筆者并不特別強求分別獨立測試。只要有效地覆蓋所有邏輯,將幾個(gè)對象聯(lián)合在一起測試也并無(wú)不可。
出于這樣的考慮,我們可以針對整個(gè)集成點(diǎn)的façade(即IdentityService)進(jìn)行測試。在實(shí)例化IdentityService對象時(shí),需要mock[7]其中使用的XmlEndPoint對象,以隔離“發(fā)起網(wǎng)絡(luò )請求”的邏輯:
xmlEndPoint = mock(XmlEndPoint.class);
identityService = new IdentityServiceImpl(xmlEndPoint);
然后我們就需要mock的XmlEndPoint對象表現出幾種不同的行為,以便測試IdentityService(及其內部使用的其他對象)在這些情況下都做出了正確的行為。以“查找用戶(hù)”為例,XmlEndPoint的兩種行為都是OpenPTK的文檔里所描述的:
1. 找到用戶(hù):HTTP狀態(tài)碼為“200 FOUND”,應答正文為包含用戶(hù)信息的XML;
2. 找不到用戶(hù):HTTP狀態(tài)碼為“204 NO CONTENT”,應答正文為空。
針對第一種(“找到用戶(hù)”)情況,我們對mock的XmlEndPoint對象提出期望,要求它在get方法被調用時(shí)返回一個(gè)代表HTTP應答的對象,其中返回碼為200、正文為包含用戶(hù)信息的XML:
when(xmlEndPoint.get(anyString())).thenReturn(
new XmlEndPointResponse(STATUS_SUCCESS, userFoundResponse));
原文轉自:http://www.infoq.com/cn/articles/enterprise-systems-integration-points