你的应用就绪了吗?
英文原文:Is Your Application Ready?
问题很简单,却很难回答。
我们通常会按时间交付软件,在截止时间之前努力完成所有开发和测试工作。我们会优先完成那些自己觉得重要的部分,当应用达到确定的质量标准后, 就准备上线了。因为发布的内容可能不会尽善尽美,所以我们总是假设将来还有一些版本。甚至我们交付软件时,都不能辨别应用的就绪状态。我们总是依赖于测试 人员给我们答复,但为了产品的收益和质量,我们应该让所有人都参到这个过程中。
在本文中,我们将会围绕发布一个工作应用程序讨论当前流行的几种不同测试方法。然而,这些不是你“常规”的测试工作!我们强调质量不能变成最后打补丁。我们的目标是在整个编程过程中打造并保证质量。
测试简史
现在我们的开发项目中有作为职能小组的测试人员,在那之前软件的用法都很简单,所以只让开发人员确保软件可以工作就够了。但是随着项目越来越 大,应用变得更加复杂,发布截止日期也更紧了。因为程序员永远都不够用,而且会越来越紧张,所以就要求他们在很短的时间内开发更多的特性。在巨大的“质量 洼地”里长满了虫子。我们急需杀虫剂。
测试人员将成为那些杀虫剂。随着职责流转,测试人员将成为守护者,捕捉从构建中遗留下来的虫子。很不幸地是,这个主意没有成功。应用太复杂了, 有太多场景需要测试人员充分覆盖。即使引入了自动化,测试花费的时间也不可能低于原型开发的时间。而虫子仍然在那儿,满地都是。
在最近几年里,特别是随着敏捷方法的发展,测试已经在生态系统中有了彻底转变,自始至终我们都不可能离开测试了。另外,从安装版软件,到客户端/服务器端,再到云和移动应用的转变,已经决定了应用是否“做好市场准备”会面临更多的挑战。
如今的测试非常复杂
通常,我们认为测试是达成可交付软件的重要环节。但是你仔细想想看,在开发过程中测试无处不在,它肯定不是一个“周期”或者一项“任务”,而是散布在整个产品研发组的一套技能。
一个简单的例子可以很好地解释为什么测试不仅仅像最初定义的那样只是检查工件。假设你确定了如下需求:记录应用中的 3 处错误将锁定用户。这看上去非常直白,但当你开始认真研究时,你会发现很多问题:什么是错误?锁定后会触发什么?这些“隐性”的需求可能并不明确,而需要 我们理解上下文中的要素。
我们来进一步说明:如果我们捕获的所有需求都是一成不变的,那么我们如何完成所有测试?你不能在生产环境中测试,所以要在一些预生产环境的服务 器中做功能测试。你需要运行集成测试用例,运行场景脚本,验证实际的运行情况,然后它自己再完成清理工作。我们也可以在单元测试层面测试这些用例,但需要 模拟数据库和环境调用。
那只是一个简单的需求,在测试人员实际操作之前就已经都完成了。
反馈回路
在敏捷时代V模型已经过时了,但即使在今天仍用它来表述一些基本的概念,说明过程中的每个步骤有哪些验证操作的测试点。
如今我们叫它们“反馈回路”,可以用戴明的 PDCA 循环描述。
我们在敏捷开发中尝试把这些循环周期变得尽可能简洁。瀑布项目的问题是循环周期太长,反馈周期超过一周、一月甚至一年。然而,每个开发人员隔几分钟都要编译自己的代码,他们很清楚:循环周期越短越好。
在不同的情况下我们可以灵活使用这些循环。然而,当我们想要验证一个工作特性时,就遇到了这样的现实。
较短的反馈循环不会“恰好发生”。我们需要把它们应用到实践中。这给时断时续的“即时编译”带来了可能性,因为:
- 我们有促成这种可能性的工具。高效的编译器大大加快了反馈速度。
- 我们清楚反馈工作的意义,为此创建了一个专门用于反馈的系统。我们的程序每隔几分钟就会自己去点击“编译”按钮。甚至如果我们在适当的时间内没获得反馈都觉得不太正常了。
当代码编译的速度相当快的时候这是成立的。如果高效的、轻量级的编译器需要几个小时的编译时间就太慢了。这样我们就无法经常执行它,无法得到反馈,只好从头再来。
通过隔离快速反馈
为了获得较短的反馈回路,我们需要削减关注点。例如,为了避免过长的编译周期,我们可以执行增量编译。我们可以只编译变更的部分,而不必等着完 整的编译都结束。这其实是风险的权衡:我们为了反馈速度牺牲了反馈质量。虽然系统在完整编译完之后可能会有不同的表现,但是我们假定这些差异并不大。我们 在这种假定的前提下去缩短反馈回路。
以隔离系统和过程的某些部分去获得更快的反馈并不是新的观点,在开发过程中出现了很多这样的想法。即使我们可能不使用这些术语(比如在增量编译这个案例中),但是会用于其他的场景下,比如需要快速验证反馈的时候。
验证和隔离
构建正确的应用:构建错误的产品是最大的浪费。我们要在产品开发初期识别正确的需求,甚至可以先做一个原型 系统。产品人员使用各种各样的工具(包括实体模型、图纸和原型)去收集需求和反馈意见,验证还在正确的轨道上继续开发,或者要做战略调整(精益创业术 语),改变方向,再次收集反馈,最终回到正轨。
注意这种验证与原先的V模型需求验证的差异:我们验证是否构建了客户所需的,而不是验证是否正确构建了规定的需求。在初期,我们不需要实际的应用。以后,我们要不停地收集数据。为了持续地开发和完善,我们需要在实际系统中不断地收集应用数据。
功能性单元测试:这是非常典型的隔离性应用案例,因为如果不具备好的隔离性,很多功能都无法快速测试。不管你使用模拟框架、依赖注入容器还是简单的 TDD 和抽象依赖,归根到底都是把测试从环境中隔离出来。单元测试背后的思想是得到快速反馈,所以显然要拥有可以达成这一目标的工具。
如果你考虑单元测试的流程和它所要达成的目标,你就会发现以隔离性为基础构建软件(例如,SOLID 原则)还有其他好处。我们通常把应用的易维护性看作开发团队增加新特性、修复缺陷或修改设计的能力。如果流程中没有快速的测试反馈机制,那么就会面临风 险,白白浪费时间。
功能性集成测试:我们做单元测试的时候会导致与增量构建相似的后果:快速反馈的代价就是增加了系统内流程未充分测试的内在风险。集成测试更像完整的构建:我们在获得高质量反馈的同时也要承受“慢”的代价。我们要花时间准备和执行测试,但它们却能让我们最终以更好的视角去了解系统的性能。
我们有时会试图隔离系统的各个方面去缩减这些时间。所以我们可能要模拟浏览器操作,在 UI 层下运行包括数据库代码的流程。或者,我们可能要在图形用户界面(GUI)中执行不涉及数据库的测试;这需要在反馈质量和速度之间作出取舍。
通讯测试:虽然它是一种特定的集成测试,但通讯测试需要特别拿出来讨论,在此我分享一下我的个人经历:十年 前,我们团队正在开发一个用于大量硬件软件接口。这个接口要实现基于 TCP 和 UDP 的通讯。但我们开发的时候硬件还没有到位。当然,我们可以等,但是我们不想因此影响进度。我们确定了消息信息和结构、通讯连接和恢复以及错误处理机制,以 构建网络模拟器。在那时它还不是自动化的,只是一个展示接收信息的应用,能够针对请求向我们的软件发送返回消息。后来我们增加了一些用于不同场景的自动化 处理,比如信号交换。
有了这个模拟器不仅加快了我们的开发和集成速度,它还向我们反馈了通讯组件的开发完成情况,记录了出现的故障,还能当作参照工具来用。
非功能性需求:很久以前,我们只关心质量。然而今天,我们取得了长足的进步,现在需要关心更多东西,包括:
- 可扩展性:某些框架的设计目的是扩展和自定义。我们通常为用户提供一些 API,用户可以用这些 API 扩展一些我们原来没有想到的功能。当我们确认系统扩展性的时候,创建了用于扩展的模拟器(有时是真正的组件)去检验它的运行情况。
- 安全性:有时我们的应用有硬性的安全需求,但是大多数情况下安全性是后期添加的要求,它的难易程度完全取决于每个独立的开发人员。我们可以使用静 态代码分析工具或请外界专家对应用做威胁评估。但很不幸地是,只有在与产品类似的环境下才能执行完整的安全扫描。除非那种像过渡场景下的环境,但这也意味 着还要针对现场服务器做最终的测试。
- 可扩展性和性能:特别是服务器端的应用,我们需要评估的应用的能力不仅是大量请求的响应,还包括未来请求增长时的执行情况。我们使用工具对系统做 压力测试,根据工具的反馈结果了解系统的性能。此外,我们希望提前预知,为此我们会在一个孤立的系统中执行测试,而不是在会影响到实际用户的服务器上。
- 可用性和可靠性:系统性能很重要,但可用性是庞大用户的基础。此外,我们还要评估应用经受宕机以及其恢复的能力。我们使用压力工具在独立的服务器上执行测试用例,应用针对这些用例反馈行为表现。
- 可移植性:在移动领域,我们习惯在多种设备上测试我们的应用。每种设备会有不同的操作系统、内存、资源和性能。随着每种新设备的上市,在多种设备 上完成测试已经成了一种挑战。我们为了在短时间内保持有效性一直都在不懈地努力,现在正致力于模拟器的方向。我们用软件模拟器代替物理设备,确认应用能够 在多种设备上正常运行。
用户体验:最后这一点对于人类用户来说是无可替代的。这需要测试人员的参与。测试人员站在用户的角度发出用户的声音,回答像这样的问题:这有益吗?我能达成目标吗?这就是我们测试的实际系统。
当剩下的测试都自动化了,那么手工探索性测试就是最后的难题了。测试人员批准之后它就可以上线了。
总结
产品研发复杂且具有风险。我们要确保构建了适当的特性、确保功能的正确性,并为灾难和成功做好准备。理解早期反馈工作和越来越多可以做的测试,在交付之前回答我们的问题:是的,它已经就绪。
关于作者
Gil Zilberfeld 从小就从事软件工作。他做过 20 年的商业软件开发,具有丰富的软件方法和实践经验。Gil 是 Typemock 的产品经理,在敏捷公司内从事敏捷团队的部分工作,研发敏捷开发工具。他推动单元测试和其他设计实践、可落地的敏捷方法,以及某些非常酷的工具。除了每月的在线网络研讨会,Gil 还在国际演讨会上做单元测试、TDD、以及敏捷实践和通讯的演讲。他在业余时间里还打打僵尸,呵呵,开个玩笑。Gil 的博客包括不同的敏捷专题:流程、通讯和单元测试。