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