當mock的XmlEndPoint對象被設置為這樣的行為,“查找用戶”操作就應該能找到用戶、并組裝出合法的結果對象:
Customer customer = identityService.findByEmail("gigix1980@gmail.com");
assertThat(customer.getFirstName(), equalTo("Jeff"));
assertThat(customer.getLastName(), equalTo("Xiong"));
userFoundResponse所引用的XML字符串中包含了用戶信息,當XmlEndPoint返回這樣一個字符串時,IdentityService就能把它轉換成一個Customer對象。這樣我們就驗證了IdentityService(以及它內部所使用的其他對象)的功能。
第二種場景(“找不到用戶”)的測試也與此相似:
@Test
public void shouldReturnNullWhenUserDoesNotExist() throws Exception {
when(xmlEndPoint.get(anyString())).thenReturn(
new XmlEndPointResponse(STATUS_NO_CONTENT, null));
Customer nonExistCustomer =
identityService.findByEmail("not.exist@gmail.com");
assertThat(nonExistCustomer, nullValue());
}
其他操作的測試也與此相似。
集成測試
有了上述兩個層面的測試,我們已經能夠對集成點的五個組件完全覆蓋。但是請勿掉以輕心:100%測試覆蓋率并不等于所有可能出錯的地方都被覆蓋。例如我們前述的兩組測試就留下了兩個重要的點沒有得到驗證:
1. 真實的服務所在的URL;
2. 真實的服務其行為是否與文檔描述一致。
這兩個點都是與真實服務直接相關的,必須結合真實服務來測試。另一方面,對這兩個點的測試實際上描述功能重于驗證功能:第一,外部服務很少變化,只要找到了正確的用法,在相當長的時間內不會改變;第二,外部服務如果出錯(例如服務器宕機),從項目本身而言并沒有修復的辦法。所以真正觸碰到被集成的外部服務的集成測試,其主要價值是準確描述外部服務的行為,提供一個可執行的、精確的文檔。
為了提供這樣一份文檔,我們在集成測試中應該盡量避免使用應用程序內實現的集成點(例如前面出現過的IdentityService),因為如果程序出錯,我們希望自動化測試能告訴我們:出錯的究竟是被集成的外部服務,還是我們自己編寫的程序。我更傾向于使用標準的、接近底層的庫來直接訪問外部服務:
System.out.println("=== 2. Find that user out ===");
GetMethod getToSearchUser = new GetMethod(
configuration.getUrlForSearchUser("gigix1980@gmail.com"));
getToSearchUser.setRequestHeader("Accept", "application/xml");
httpClient.executeMethod(getToSearchUser);
assertThat(getToSearchUser.getStatusCode(), equalTo(200));
System.out.println(getResponseBody(getToSearchUser));
可以看到,在這段測試中,我們直接使用Apache Commons HTTP Client來發起網絡請求。對于應答結果我們也并不驗證,只是確認服務仍然可用、并把應答正文(XML格式)直接打印出來以供參考。如前所述,集成測試主要是在描述外部服務的行為,而非驗證外部服務的正確性。這種粒度的測試已經足夠起到“可執行文檔”的作用了。
持續集成
在上面介紹的幾類測試中,只有集成測試會真正訪問被集成的外部服務,因此集成測試也是耗時最長的。幸運的是,如前所述,集成測試只是用于描述外部服務,所有的功能驗證都在網絡端點測試(使用Moco)及其他組件的單元測試中覆蓋,因此集成測試并不需要像其他測試那樣頻繁運行。
Maven已經對這種情形提供了支持。在Maven定義的構建生命周期[8]中,我們可以看到有“test”和“integration- test”兩個階段(phase)。而且在Maven項目網站上我們還可以看到一個叫“Failsafe”的插件[9],其中的介紹這樣說道:
The Failsafe Plugin is designed to run integration tests while the Surefire Plugins is designed to run unit tests. The name (failsafe) was chosen both because it is a synonym of surefire and because it implies that when it fails, it does so in a safe way.
按照Maven的推薦,我們應該用Surefire插件來運行單元測試,用Failsafe插件來運行集成測試。為此,我們首先把所有集成測試放在“integration”包里,然后在pom.xml中配置Surefire插件不要執行這個包里的測試:
org.apache.maven.plugins
maven-surefire-plugin
${maven-surefire-plugin.version}
default-test
test
test
**/integration/**/*Test.java
再指定用Failsafe插件執行所有集成測試:
maven-failsafe-plugin
2.12
**/integration/**/*Test.java
failsafe-integration-tests
integration-test
integration-test
failsafe-verify
verify
verify
這時如果執行“mvn test”,集成測試已經不會運行;如果執行“mvn integration-test”,由于“integration-test”是在“test”之后的一個階段,因此兩組測試都會運行。這樣我們就可以在持續集成服務器(例如Jenkins)上創建兩個不同的構建任務:一個是提交構建,每次有代碼修改時執行,其中不運行集成測試;另一個是完整構建,每天定時執行一次,其中運行集成測試。如此,我們便做到了速度與質量兼顧:平時提交時執行的構建足以覆蓋我們開發的功能,執行速度飛快,而且不會因為外部服務宕機而失敗;每日一次的完整構建覆蓋了被集成的外部服務,確保我們足夠及時地知曉外部服務是否仍然如我們期望地正常運行。
原文轉自:http://www.infoq.com/cn/articles/enterprise-systems-integration-points