验收测试让交付团队超越了基本的持续集成,即验证应用程序是否为用户提供了有价值的功能。不过对于刚开始尝试部署流水线的团队来说,想要自动化验收测试,需要跨过三大门槛。

  一是实现和维护验收测试的技术门槛。理想情况下,验收测试好可以模拟用户与应用程序的真实交互,因此如果有图形界面的话,验收测试理应通过这个界面和系统打交道。然而,直接通过GUI进行测试会遇到几个问题:界面变化速度很快、场景的准备相对复杂、拿到测试结果较难等。比如一个典型的WEB应用程序,如果通过GUI测试,那么一般需要解析HTML标签来填写参数,提交表单,后再次通过解析来获取系统的返回值。如果测试代码中充斥着操作HTML的细节,测试的可读性会大大下降,验收测试本身也更脆弱,在需求变更时反而会拖慢进度。

  二是交付团队工作方式的变化。在传统团队中,需求分析、开发和测试是独立而又顺序的过程。算能形成详细的需求文档,三方对同一段文字可能都有自己的理解。结果经常出现偏差,需求分析人员抱怨开发人员没有正确理解需求文档,开发人员抱怨需求文档不清晰、抱怨测试人员故意挑刺。敏捷实践和验收测试的出现缓解了这一问题,通过预先定义验收规格,减少文字上的误解,明确了开发工作的完成标准。不过这种思维方式的转变很难一蹴而,需要交付团队及其利益关系人共同持续努力才能成功。

  三是对组织的环境、配置管理及部署流程的挑战。当引入自动化验收测试后,对整个部署流水线的自动化程度会有更高要求。比如部署流水线应该能够自动将应用程序部署到待测试的环境中。如果应用程序依赖数据库,那么还应该能够部署数据库schema。另外一些运行时配置也需要通过脚本完成设置。这当中除了脚本准备之外,组织的环境管理也是要能跟上的。一般情况下,稍微大一些的组织都是有专门的运维团队(而非交付团队)来管理硬件设备和其配置的。因此,这个问题一般也涉及多个团队来协作解决。

  面对这三座大山和进度压力,新手团队可能会感慨“信息量略大”而止步不前。这时不妨考虑各个击破,三个问题中的工作方式转变涉及的利益干系人多,难度也大;环境管理问题虽然涉及不同团队,但一般还是技术部门内的问题,关起门来好商量;验收测试的实现/维护主要是技术问题,相对简单。如果时间和资源确实有限,不妨考虑牺牲一部分验收测试的有效性,采用简单的非端到端验收测试,在自动化部署流程方面也可以做一些折中,集中力量转变工作方式。当整个工作已经进入节奏,再去改进某个具体环节时顺利很多了。团队只要愿意迈出一小步,也能获得很大的价值。

过渡方案:相对简单的非端到端验收测试
  如果团队的技术积累还不足,又没有足够的资源,不妨考虑简单一些的验收测试策略作为过渡方案。非端到端的验收测试是指直接调用应用程序内部的逻辑结构来驱动测试。由于测试代码和产品代码都使用同一种语言编写,可以省去比较繁琐的数据格式解析。而在准备测试数据和场景时,直接调用内部逻辑块一般也更方便。以典型的使用SpringFramework的Java WEB应用程序为例,团队可以采用和集成测试类似的基础架构来编写非端到端的验收测试。

  这里所说的集成测试的目的是验证应用程序与外部服务的连接能否正常工作。这与应用程序实现的具体功能关系不大,因此一般只加载必需的ApplicationContext。

图表 1 集成测试

  非端到端的验收测试可以采用和集成测试一样的测试基础架构,这样你可以使用熟悉的测试库了,不同的是需要加载整个ApplicationContext以尽可能模拟应用程序被部署后的情况。

图表 2 加载整个上下文

  由于可以访问整个ApplicationContext中的任一对象,我们可以通过访问应用程序的内部组件来执行测试,比如应用层的某个Service。但需要注意的是,选取的组件离UI层越远,其模拟真实用户交互的有效性越差,而且受内部实现变更的影响越大。如果应用程序使用spring-webmvc的3.2以上版本,推荐使用它的mvc测试库。spring-test-mvc提供了类似http请求的DSL,此时虽然测试还是基于ApplicationContext,但并不直接访问内部组件了。这个方案对于新手团队比较友善,但请注意,这仅仅是个过渡方案,因为:

  非端到端测试无法提供全面的回归测试,尤其是UI操作。在好几个项目中,我们发现仅采用非端到端测试覆盖的功能,团队不得不保留手工回归测试。如果UI上包含了大量复杂的控制逻辑甚至有业务逻辑泄漏到UI组件中,这会稀释验收测试带来的收益。

  由于测试加载的ApplicationContext和Web容器加载的ApplicationContext存在差异,非端到端测试可能会漏掉一些问题。比如在非端到端测试中一次性加载了booking-servlet.xml和root.xml,使他们成为了一个整体的上下文,而实际上在Web容器中并不完全是这样,root.xml中的bean并不能访问和控制booking-servlet.xml中的bean。一个常见问题是如果在booking-servlet.xml中需要使用占位符,而恰巧我们已经在root.xml中有一个现成的<context:placeholder/>,看起来水到渠成,而且在测试中也没有问题,但实际部署到web容器时,会加载失败。

  非端到端的验收测试不能作为任务完成的终标准。因为还有UI部分还没有完成。当这类验收测试通过时,我把这个任务称作“可以进入UI调试的”。

  因此,如果团队有足够的技能和资源时还是应该直接使用端到端的验收测试,尤其当应用程序提供API(比如WebService)或是采用更易于解析的数据格式与客户端交互时。比如如果应用程序提供了基于JSON的API,完全可以使用http-client来驱动测试。