软件测试反模式——杯型蛋糕

要想帮助团队制定测试策略,编写出可靠可伸缩的测试,测试金字塔是最好的方式之一。 根据我多次的使用经验来看,它真的非常有用

同时,我也经常会看到有的团队在尝试实践测试策略时掉进各种陷阱里。正如Alister Scott指出的,一个常见的陷阱就是冰淇淋甜筒状的反模式(anti-pattern)。这种模型形容在没有足够多的底层测试(单元测试、集成测试和组件测试)的情况下,创建了太多GUI(图像用户界面)测试,甚至更多的手动测试。

得益于自动化测试在软件开发界的普及,这种反模式现象正在减少。并且,加上TDD(测试驱动开发)和BDD(行为驱动开发)这些实践的大力推广和应用,我已经有很长一段时间没看到团队担心过底层测试(单元测试、集成测试、组件测试)了。

然而,与此同时我还观察到一些团队跌进了另一个非常危险的陷阱。这个新的反模式陷阱有如下非常明显的特征:

  • 不同的团队写不同层次的测试。
  • 一般由如下三类团队来写不同的测试:
    • 开发人员写单元测试,集成测试和组件测试
    • 另外一个团队通过界面来做黑盒测试
    • 手动测试员进行一系列手动测试来测试功能
  • 通常这些团队各自独立工作,合作甚少。
  • 整个项目的工作流程流水式进行,并没有同步工作。首先是开发人员写出代码和对应的测试,然后测试人员手动地测试功能,之后GUI测试人员才编写他们的测试。这一流程看起来像什么?一个小型瀑布。
  • 这三个团队在某场景是否应该被测试,或自动化测试的级别上,无法达成一致。这就导致了重复——相同的场景在不同级别上都进行了自动化测试。

在和同事PatrickTarso讨论此事时,我们对比了一下这个新的陷阱和之前提到的冰淇淋甜筒模型,然后开始思考这个新的反模式像什么。大致说来,它应该有个庞大的底部,宽阔的中部和一个巨型的顶部。Tarso灵光一闪,突然说道:“这不就是个杯型蛋糕(Cupcake)吗!” 他说得简直太对了。

 

fabiocupcakenew1_0_04444aff9e8be210d16a68f29a20fe7a

 

下面介绍一下软件测试中的杯型蛋糕反模式:

这里有一些小贴士可以助你避开这个杯型蛋糕,甚至很有希望将其“扭转”回理想的测试金字塔模型:

  • 合作:允许团队之间进行合作,然后讨论在哪一层写特定的测试才是最好的。以下是一些实用手段:
    • 同步工作:当开发一个功能时,鼓励不同的团队之间同步工作,而不是线性工作,各自为政,像一个迷你小型瀑布一样
    • 跨角色结对:支持跨角色结对。比如,在一个Story快完结时,一个开发者可以和一个测试人员结对,决定在哪儿执行自动测试
    • Story Kickoff:这里有很多方法可以采用,比如三驾马车(The Three Amigos),或者有些人称之为story kickoff,目的都是为了帮助团队分享对需求的理解,减少沟通隔阂
  • 从最底层开始测试:在条件允许的情况下,从最靠近代码的地方开始测试一个功能,降低测试的深度
  • 如果可能,尽可能合并团队:有时你并不需要不同的团队,你所需要的只是在不同职位上的不同的人。比如说一个开发者可以成为某个功能的界面测试人员,即使如果他(她)并没有参与过开发这项功能
  • 在目标上达成一致:确保每个人都有相同的目标。比如,整个团队要对什么是“完成”达成一致意见,而不是一起工作“完成开发”或者是“完成测试”
  • 同时要达成一致的,还有衡量测试工作的方式。而一旦合适的衡量方式确立了,就要避免只能应用于某一特定层次的“横向衡量”,比如靠自动化测试的用例数目来衡量界面测试的质量。更合适的衡量方式应该是“纵向”的,这样就能把所有层次都囊括进来。就用上面的例子来讲,衡量方式应该改成这样——每个story不管在哪一层(UI测试、单元测试等)都至少需要有90%的自动化测试覆盖率。如此一来,衡量方法就被共享了,达到了双赢的效果

 

fabiocupcakenew4_49b01336bfc161b4e720e3e0aee6696b

 

当一个由开发者、手动测试人员和界面测试人员组成的团队,为了达成同一目标,齐心协力相互帮助的时候,我确信这个团队能够完成更好的测试策略,更好地确保软件质量。

最后,你有什么想法?

Share

我和敏捷团队的五个约定

我——作为一名测试人员——有一个与众不同的习惯:每当要加入一个新项目的时候,我总会找到项目中的同伴,真诚而亲切地说:“为了更好地合作,我有5个约定,希望大家能尽量遵守”。

约定1. 业务分析师们,我们其实是同一个角色的两种面孔,请叫上我们参加客户需求会议

我们的团队需要让客户频繁的得到可用的软件,客户的不断反馈会给软件的未来做出最正确的方向指引。

如果我们交付的软件有很多质量的问题,存在大量的缺陷,客户会被这些缺陷的奇怪行为干扰,没有办法把注意力放在软件本身的价值是否符合他们的真正需求上, 不能给出最有价值的反馈。所以,我们只有频繁的做测试,在每次交付之前都把质量问题找出来告诉我们的团队,问题才能及时的得到改正。

而我坚信“prevention is better than cure”(预防胜于治疗),我会要把工作的重点放在预防缺陷上,这样可以节省Dev们很多修复缺陷的时间与精力。

为了达到这个目的,我需要跟你一起参加客户需求会议,尽早的了解客户需求与使用软件的惯常行为。那么在你完成需求的验收条件的定义的时候,我也基本完成了测试用例的准备。

我们可以赶在开发人员们写代码之前就告诉他们我要测什么,让他们减少因为过于乐观而漏掉的一些重要的有破坏性的情况,减少缺陷的发生。这是我测试的一项重要任务。

如果你们在大部分需求都整理好了再交给我们,我会浪费掉等待的时间。更重要的是,开发好的软件里面已经有很多本来可以不存在的缺陷在里面了,开发人员们可能需要加班加点来保证在项目最终交付时间之前把它们改好。这样很容易产生新的缺陷的。

所以,请让我尽早了解需求,请不要让我到项目后期才能开始测试。

约定2. 开发人员们,虽然你们是编写自动化测试的专家,但请听听我们意见

我知道,对于你们,自动化测试不过是利用junit, rspec, selenium,watir,uiautomation等等写出的“另一段程序”而已。而对于80%的QA来说,编写自动化测试并不是一件简单的事情。

不过我仍然相信,有测试人员介入的自动化测试更有价值。

你们用单元测试,集成测试来保证代码的质量。然而你们的这些日常测试离代码更近,离最终用户还点远。很多测试都不是在测软件功能。

你们可以把功能测试写的又快又多,而我们可以指出什么功能测试最有必要加到自动化测试中。

你们平时大部分精力都在编码上,没有太多时间去查都有什么缺陷。而我们可以指出什么地方缺陷可能会出现的比较频繁,建议在这些脆弱的地方加自动化测试。

所以请听听我们的意见,我们可以给你们提供这些信息。

约定3. 项目经理们,请不要要求我们测试软件的所有路径

软件测试是一个永无止尽的任务。基本上没有什么软件简单到我们能够尝试完它的每一个可能的路径的。就连一个看似简单的微软计算器都有无穷尽的路径,无止尽的输入,更何况比这个更复杂的商用软件。

如果你们担心没有尝试过全部的路径不可靠,疑惑我们怎么敢说这个软件质量是好的还是坏,都有什么风险。请你们先注意,我们是跟业务分析师一样,都了解软件的价值的。价值可以帮我们做出判断,什么时候可以停止测试并对客户说我们的软件已经满足您的要求了,请放心使用。

因为我们了解价值,我们可以肯定的说哪些软件的使用方式是至关重要的,哪些是不太可能出现的。我们会在全面测试了软件以后,把主要精力放在价值高的功能点上。合理的利用项目有限的时间。

因为我们了解价值,我们可以正确的把发现的问题分类。我们可以帮助dev们把精力放在重要的缺陷上,避免把时间放在对于客户微不足道却不得不花费大量精力才能修正的问题上。

所以,请不要要求我们无止尽的测试一个软件。我们了解价值,请相信我们的判断。

约定4. 迭代经理们,如果对于交付风险有任何疑问,请来询问我

BA和Dev们都是关注一个软件在什么情况是可以良好的工作。而我们除了验证这些情况以外,大量的时候都用在寻找什么样的情况软件不能正常的运行。所以除 了针对定义好的软件行为进行测试,我们还会做很多探索性测试。我们通常可以通过这样的测试发现一些没有定义的、不曾预期的行为。这些行为往往将会构成软件 交付的风险。

我们会告诉你们现在都发生了什么问题,分别分布在哪里。

我们会告诉你们,在什么情况下软件可能会有异常行为,是不是会牵连到其他的部分,是否可以绕过去。

我们会告诉你们,哪些部分功能比较不稳定,需要更多的留意。

约定5. 测试人员们,那些敏捷实践对于我们也是有用的。

结对不是dev们的专利。我不希望总见到你们独自坐在自己的位置上冥思苦想。走出去,跟其他队友多多交流!

多跟测试队友交流,pair看看设计的测试用例是不是够全面,独自一个人想到的未必足够好。他们会给你诚恳的意见的。对他们,也请一样认真对待。

如果你发现开发人员们做出的架构决定使测试工作变得更困难。那么请大声地告诉他们,design for testability(提高你们设计的可测性)。

如果你发现业务分析师写的需求无法验证,定义的客户行为不够具体,一个用户故事中包含太多了功能点,等等,那么也请大声地告诉他,INVEST(独立,可协商,价值,可估算,短小,可测)。

也请你们多跟开发人员结对写自动化测试,既可以帮助你们学习怎样更好的编写自动化测试,也能帮助开发人员们结对更多的了解用户行为。

这就是我的五个约定,它们是我在团队中顺利展开工作的基础。


作者:覃其慧,ThoughtWorks敏捷咨询师。她参与了大量的敏捷软件开发的实践和敏捷咨询。目前主要关注以价值为驱动的敏捷测试。


本文原文发表于InfoQ:http://www.infoq.com/cn/articles/thoughtworks-practice-parti

Share

敏捷测试之借力DSL

随着敏捷越来越广为人知,敏捷测试也更多受到了大家的关注。在这里,我想谈一下我在敏捷项目中遇到的一个自动化测试相关问题以及我们如何借助DSL领域专用语言来解决它。

对敏捷软件开发方法有一定了解的人都知道,敏捷软件开发过程是一个迭代式交付的过程。每个迭代相当于比较小型的交付周期。那么,为了配合频繁的软件交付,敏捷测试相对于传统测试必须要做相应的调整。这也导致了敏捷项目中的测试面临几个特有的挑战:

  1. 频繁的回归测试以确保每个迭代的成果都是可交付的
  2. 让整个开发团队参与到测试活动中以缩短质量信息的反馈周期
  3. 让客户参与到测试活动中来帮助提高测试的有效性

自动化测试在应对频繁的回归测试这个挑战上起着非常关键的作用。自动化测试做不好,团队最终会被每个迭代都会增加的回归测试工作量压垮。我经历过的一个团队,在这个团队中,大家很早就意识到了自动化测试的重要性,在自动化测试上的投入不遗余力。我们相信自动化功能测试增加到足够多的时候,它就能指导手动回归测试,保证整个交付过程顺利进行。

的确,自动化测试刚开始进行的时候,我们收益颇多。每增加一个自动化测试,我们就能减少一些手动测试。自动化测试让我们我们有比较充裕的时间来手动测试那些还没有来得及自动化的、难以被自动化的功能点上,而且还能有时间和精力做探索性测试。这个结果让团队感到生活很美好,也让我们对自动化测试坚信不疑。

然而好景不长,随着自动化测试的不断增加,我们会面临这样一些问题:

  1. 自动化测试是围绕着实现细节展开的。随着数量的增多,业务的轮廓很容易迷失在细节中。
  2. 在功能级别丧失了对测试的追踪。由于测试人员无法具体知晓那些测试案例被自动化测试覆盖。每次回归的时候,团队都需要回归整个测试组。

于是,我们的手动测试越来越难得到自动化测试的帮助。它开始成了项目的鸡肋。测试代码阅读困难、维护困难以及测试结果的看起来也很费劲。这直接导致了我们不仅要投入相当的时间来增加自动化测试,也要投入不少时间来阅读并利用测试结果。

于是我们开始重新审视自动化测试的做法,继续摸索更好的方式。

很快,我们发现“能够跑起来”并不是好的自动化测试仅需的特性。让我们通过一段测试代码来看一下具体怎么回事。

selenium.open(“/”)
selenium.type(“id=username”, “myname”)
selenium.type(“id=password”, “mypassword”)
selenium.click(“id=btnLogin”)
selenium.waitForPageToLoad(30000)
assertTrue(selenium.isTextPresent(“Welcome to our website!”))

这个测试中,我们首先打开了一个页面,在页面中寻找一个id为username的输入框,输入“myname”,然后再寻找一个id为password的输入框,输入“password”,然后点击一个id为btnLogin的按钮,等待30秒以后,断言页面应该出现的文字。

我们可以看到,这个测试的实现很完整的描述了测试的操作过程,是一个面向步骤而不是目的的描述。当然,稍加分析,我们也可以看出来这个测试的目的是测用户登录成功系统。

但是,想象当我们有很多这样面向步骤来描述的测试时,要从中抽离出被无数细碎的操作步骤所淹没的测试意图,并把测试的结果利用起来,其实并没有那么直观。而且,如果在测试中出现了错误,对于问题的具体功能点的定位也不是那么容易。

与此同时,并不是团队中所有的成员都有能力阅读和编写这样的测试。这无疑降低了团队成员对于自动化测试的参与度。对于客户,自动化测试更是一个黑盒子,做了什么,没做什么,基本上搞不清,更谈不上参与到自动化测试中,帮助提高测试的有效性。

种种状况,究其原因就是测试可读性太差,测试意图不够明显。可运行并且容易读的测试才是好的自动化测试。这样才能够保证任何时候,我们不会丧失对于测试案例的跟踪与管理。测试人员随时都可以通过快速阅读测试,了解那些功能已经被自动化测试覆盖,有效规划手工测试的工作量。

怎么提高测试的可读性呢?

我们的解决办法是DSL领域专用语言。

什么是领域专用语言?在马丁大叔的博客里有比较详细的描述。大致来说,领域专用语言就是针对某个领域的特定目的编程语言。不像Java、C#等通用语言,可以解决任何领域的问题。领域专用语言通过自己独特的语法结构来描述更接近于专业领域语言的业务。

让测试的描述能够接近被测系统的领域语言、使测试意图得到清晰表达就是我们想要得到的效果。DSL正好能够帮我们实现。

让我们再看看之前的那段代码:

selenium.open(“/”)
selenium.type(“id=username”, “myname”)
selenium.type(“id=password”, “mypassword”)
selenium.click(“id=btnLogin”)
selenium.waitForPageToLoad(30000)
assertTrue(selenium.isTextPresent(“Welcome to our website!”))

由于使用的是通用语言,在我们这个特定的使用场景中显得过于细节化、过程化,不能清晰表达测试意图。

换成DSL,我们的测试就可以直接用验收标准的语言来描述如下:

Given I am on login page
When I provide username and password
Then I can enter the system

这样测试的内容就直观多了,还包含了一些业务信息,让我们知道这个是在测试一个登录的场景,而不是任意的输入信息,兼顾传递了业务知识的职责。至于这些DSL背后能够运行的代码,也被隐藏起来。如果是不能够阅读原来那样的测试代码的人(不管是需求分析人员还是客户甚至一些对自动化代码关注比较少的测试人员)想要加入到自动化测试活动中进行反馈,就不会被DSL背后的代码带来的“噪音”所影响。

当然,在我们的现实应用场景中,这个需求没有那么简单,我们的验收标准还会考虑不同的数据比如输入不同组合的用户名密码:

Given I am on login page
When I provide ‘david’ and ‘davidpassword’
Then I can enter the system
Given I am on login page
When I provide ‘kate’ and ‘kate_p@ssword’
Then I can enter the system

以及更多的测试数据。

那么这种情况下,仅仅是比较通俗的语言还是不够的,毕竟测试数量在那摆着。如果测试数量不能减少,维护起来仍然很麻烦。打个比方,如果系统的实现变成了每次都要输入用户名、密码和一个随机验证码,我们就需要在我们的自动化测试中修改多处,比较繁琐。因此,我们需要在可读性比较好的自然语言描述的测试上,把它的抽象层次再提高一点。

幸运的是,我们当时选择的DSL工具是cucumber,它除了提供了几个测试的描述层次:Feature,Scenario,Steps,还提供了非常好的一种组织方式—数据表。

这样,我们的这个自动化测试就可以把之前的那个登录的功能根据特性、场景总结和具体的步骤分离开来,清晰的分层,同时利用数据表我们的测试精简成一系列被重复多次但输入数据有所变化的操作过程,如下:

Feature: authentication
In order to have personalized information
I want to access my account by providing authentication information
So that the system can know who I am
Scenario Outline: login successfully
Given I am on login page
When I provide ‘<username>’ and ‘<password>’
Then I can enter the system
Examples:
|username |password |
|david |davidpass |
|kate |kate_p@ssword|

测试这下看起来就更清爽了。首先,用Feature关键字,我们把测试分类到login这个大特性下的,并对这个特性本身的业务目的进行相关描述,带进业务目标,传递业务知识;然后用Scenario关键字来提高挈领的标明我们这个测试场景中做的是测试登录成功的情况,并且把步骤都写出来;最后,我们用Examples关键字引出具体的数据表格把用到的数据都展示出来,避免我们的相同步骤因为测试数据的变化而重复若干遍造成冗余。万一碰上了需求的变化,要求同时提供用户名、密码和验证码,那我们的测试也只需要改动较少的地方就足够了。

更棒的是,用了这种数据表的方式,整个团队的协作效率提高了。对于写代码没有那么顺畅的测试人员来说,增加自动化测试也就是增加更多测试数据,填充到数据表里就可以了。

就这样,我们用DSL实现了可执行的可读性高的文档。帮助了回归测试,降低了文档维护难度,也促进团队成员利用测试来传递知识的积极性,让更多人能够参与到测试中。如果您的团队也遇到了类似的问题,不妨也尝试一下。


本文原文发表于InfoQ:http://www.infoq.com/cn/articles/leveraging-dsl-in-agile-test

Share