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定义的构建生命周期中,我们可以看到有“test”和“integration-test”两个阶段(phase)。而且在Maven项目网站上我们还可以看到一个叫“Failsafe”的插件,其中的介绍这样说道:
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插件不要执行这个包里的测试:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <executions> <execution> <id>default-test</id> <phase>test</phase> <goals> <goal>test</goal> </goals> <configuration> <excludes> <exclude>**/integration/**/*Test.java</exclude> </excludes> </configuration> </execution> </executions> </plugin> |