请允许我借用雷蒙德?卡佛1981年成名作《当我们谈论爱情时我们在谈论什么》的标题,作为敏捷测试专栏第四篇文章的标题。当然,也请原谅我在这个伟大标题下喃喃自语的私货。
敏捷本无定式可循,不同的组织由于文化、产品和用户等的不同,在敏捷的具体做法上自然存在很大的差异——这本是敏捷实施中常态,但最近接触到不少敏捷组织中的测试管理者和测试工程师,大家对于如何在敏捷中做“好的测试”往往争执不下,尤其对于自动化测试,看法更是差异巨大。
实际上,在之前我已经就敏捷测试中的自动化测试进行了讨论,并给出了一些我认为合适的自动化测试测试策略和方法。为什么我们还需要用一篇文章来讨论自动化测试? 自动化测试的具体做法肯定不止一种,自动化测试的技术和方法更是多如过江之鲫,但当我们谈论到自动化测试时,我们想传达的是究竟什么信息?
Cockburn在《敏捷软件开发》中提到“沟通的成功依赖于发送者和接收者有可以引用的共同体验”,因此当我们提到自动化测试时,就一定需要有共同的词汇表。首先,什么是自动化测试?对一些人来说,一提到自动化测试,第一个闪过脑海的念头就是QTP、WebDriver等自动化测试工具,以及依据这些工具建立的针对功能或是UI的自动化测试测试脚本,但其实这些只是自动化测试中的一小部分而已,在我看来,所有“使用机器的能力”对测试进行部分或全部自动化的操作都应该被叫做“自动化测试”。
无论是针对代码中的类或是方法建立的单元测试,还是针对产品建立的需要人工参与的diff的方式,都是自动化测试的具体方法。对测试来说,自动化测试不是一个存在的目标,而仅仅是一种用以达成测试目标的手段。我们希望从自动化测试中得到什么?自动化测试覆盖率?自动化测试覆盖率是用来衡量自动化测试所占比例的一种手段,但这仍然不是我们在项目中开展自动化测试的目标。我们在项目中开展自动化测试,目标只能是“通过自动化测试提高测试或是产品的质量”。因此,在谈论自动化测试时,可以被称为目标的,只能是下面这几项:
有了目标,剩下来的就是怎么做的问题。“减少人力资源的投入”是一个最容易被直观理解的自动化测试目标,因此,不少团队的做法就是“用自动化测试替代手工测试”,将原先需要手工执行的测试用例变成UI自动化测试用例,改由自动化测试工具来执行脚本。不能说,这种方式没有效果,但由于自动化测试在识别缺陷方面的有效性远远低于手工测试(从UI测试的角度来说,一个非“预期”产生的缺陷很难被自动化测试发现,而手工测试则能轻松的发现这个缺陷),而且由于UI本身的变化性,要想达到和手工测试相同的覆盖率,单纯的UI自动化测试往往很难证明自己的投资回报。
“增加测试覆盖率”是自动化测试可以产生收益的另一个方面。容易想到的例子是性能测试和模糊测试(Fuzz testing)。依靠机器的能力,模拟大量用户的并发,以及依靠机器的能力,基于规则产生大量随机数据并发送给服务器,用于检测服务器可能的安全性问题,在这些领域,自动化测试能带来明显的收益。
但,当我说到“增加测试覆盖率”时,我所想要谈论的不仅仅是性能测试或是模糊测试这一类的针对应用某个质量属性的验证。在说到“覆盖率”时,我脑海中的另一个场景是“通过自动化测试覆盖应用中更多的接口和服务”——也就是说,通过自动化测试的方式覆盖那些在UI和功能测试无法或很难达到的部分。这一类的自动化测试可以与集成测试中的测试方式和方法进行类比,关注的是对子系统或是模块之间接口的验证。
但作为自动化测试来说,关注的不仅仅是建立对接口和服务的验证脚本,往往还需要通过推动接口和服务设计的合理性,使得建立尽可能多和尽可能完善的接口/服务的自动化测试成为可能。假设我们要对某web应用的Frontend进行接口/服务级别的自动化测试,显然,在这个Frontend应用中存在很多接口和可以被解耦的服务。如果Frontend采用了类似Clearsilver+Java的开发方式,从理论上来说,这里存在一个天然的接缝:Clearsilver作为“模板系统”,提供的主要是展现页面的能力;Java代码则主要是业务逻辑实现;Java代码通过填充模板中的动态数据项来生成用户所能看到的页面。
在这种模式下,如果能够推动应用具有良好的分层设计,使得Clearsilver端完全不包含逻辑实现,而且可以通过某种方式直接访问逻辑部分(Java代码)生成的所有动态数据,那么,在自动化测试中就可以考虑采用完全不同的方式验证模板部分和Java代码部分:用浏览器展现填充了数据的模板,基于图片比较的方式进行测试和diff;用数据验证或是diff的方式验证Java逻辑实现的正确性。通过这种方式,就能够为应用建立更多层次的验证和测试。这部分的自动化测试设计与实施通常同时伴随着可测试性设计。寻找应用中可能的测试点,改善应用的可测试性,进而为应用建立更多的不同测试的自动化测试,这就是“增加测试覆盖率”的一般方式。
“推动测试到上游”指的是将测试的执行推动到开发和设计阶段。持续集成就是一种“在上游执行测试”的方法,这种方法用定期(以天或小时为单位)的方式或是代码提交触发的方式对提交的代码进行验证,能够及时地向开发工程师提供代码质量反馈。这种方式可以保证尽早发现缺陷,且由于测试的执行者和修正者是同一个人,可以极大减少沟通带来的开销。
在我看来,类似于持续集成的方式是自动化测试能带来最大收益的做法。但要想将测试推动到上游,并非建立一个持续集成框架都足够,如果开发工程师需要花费巨大的精力来创建、执行和维护测试,显而易见,他们是不会接受这种“在上游执行测试”的理念的。Android和iPhone都提供了自动化测试框架(Android上的Instrumentation和iOS上的UI Automation),理论上来说,开发工程师可以完全自己来创建、执行和维护针对自己应用的自动化测试。
但如果考察下开发工程师在执行这些测试时的成本,就会发现,由开发工程师自行维护Android和iPhone上应用的自动化测试,实在是很难被开发工程师接受。以iOS上的自动化测试为例,如果开发工程师想要在真实的设备上执行测试,他/她必须首先找到一台iPhone设备并连接到自己的机器,编译好需要被测试的应用,用iTunes将需要被测试的应用同步到iPhone设备,然后打开UI Automation的IDE环境,加载测试脚本,执行测试,人工检查测试结果是否正确——我敢打赌,除非是独立开发者,否则没有哪个开发工程师能坚持用这种方式执行自动化测试超过一周的时间,开发工程师一定会像扔掉烫手的山芋一样把这个工作扔给测试团队。
那,怎么样才能在这种情况下把测试推动到上游?这里的主要瓶颈在于测试执行的消耗太大,如果能够建立一个移动设备的实验室,将iPhone和Android的测试执行统一为一个命令行,只要指定了被测试应用的binary、需要运行测试的设备、需要运行的测试脚本、结果存放的地方,就可以通过这个命令行直接告诉开发工程师测试的执行结果——在这种情况下,与自动化测试的维护和创建成本相比,自动化带来的收益要远远高于投入(能够得到立即的反馈,能够重复不断的快速执行测试)。
“将测试推动到上游”并不是具体的自动化测试技术,但却是非常值得尝试的自动化测试方向。在上面的iPhone和Android测试的例子中,“创建可以让测试执行成本降低的自动化测试执行环境”在这种状况下是“将测试推动到上游”的最佳选择,在其他情况下可能需要解决的是其他的问题。不管怎么说,秉承“将测试推动到上游”的观点,并通过自动化测试创造推动到上游的可能性,在我看来,是自动化测试的一个可以持续带来收益的途径。
自动化测试不是万能的,但没有自动化测试是万万不能的。和金钱一样,自动化测试不可或缺,越多越好,但也不能期望用自动化测试解决掉所有的问题。什么问题不适合用自动化解决呢?自动化测试技术是依靠机器的能力进行测试执行和验证,因此,凡是不适合使用机器执行和验证的都不适合使用自动化测试技术:例如,某些需要人工体验的操作,对音视频的流畅性的判断等等;另外,从收益上来说,还没有稳定下来的需求和UI,不适合对其进行自动化测试;又或者,在敏捷开发的环境下,测试者需要通过使用软件和探索软件等方式对每个迭代的新功能进行探索和学习。
在这些情况下,我们通常需要依赖于手工测试的手段,使用探索性测试等技术帮助我们了解应用、在脚本之外发现应用中的缺陷、以及填补自动化测试无法达到的空缺。
该在什么地方进行自动化测试?一个简单的帮助你决定是否要在某个地方引入自动化测试的判断方法是:1、从技术上来说,这里可能被自动化吗?2、如果引入自动化测试,能带来收益吗?如果两个答案都是yes,那就毫不犹豫的开始你的自动化吧。