领测软件测试网
可读性: 很多情况下,在
测试人员开始测试项目之前,公司已经有了一套
测试脚本,并且已经存在很多年了。我们可以称之为 “ 聪明的橡树 ”(wise oak tree)[Bach 1996] 。大家很依赖它,但是并不知道它是什么。由于 “ 聪明的橡树 ” 类型的测试脚本缺乏可读性,在具体应用中,那些脚本常常没有多大的实用价值,越来越让人迷惑。因此,测试人员很难确定他们实际在测试什么,反而会导致测试人员对自身的测试能力有过高的估计。测试人员能够检视测试脚本,并且理解测试脚本究竟测试了什么,这是很关键的。好的文档是解决问题的手段之一,对测试脚本全面分析是另外一个手段。由上面两种方法可以引申出其它的相关方法,我曾经在一个项目中使用过引申之后的方法。在测试脚本中插桩,把所有执行的产品相关的命令记录到日志里。这样,当分析日志确定执行了哪些产品命令,命令采用了何种参数配置时,可以提供一个非常好的测试记录,里面记录了
自动化测试执行了什么,没有执行什么。如果测试脚本可读性不好,很容易变得过分依赖并没有完全理解的测试脚本,很容易认为测试脚本实际上做的工作比你想象中的还要多。测试套的可读性提高后,可以更容易的开展同行评审活动。
可维护性: 在工作中,我曾经使用过一个测试套,它把所有的程序输出保存到文件中。然后,通过对比输出文件内容和一个已有的输出文件内容的差别,可以称已有的输出文件为 “ 标准文件 ” ( “gold file” )。在
回归测试中,用这个方法查找 BUG 是不是明智之举。这种方法太过于敏感了,它会产生很多错误的警告。随着时间的推移,软件开发人员会根据需要修改产品的很多输出信息,这会导致很多自动化测试失败。很明显,为了保证自动化测试的顺利进行,应该在对 “ 标准文件 ” 仔细分析的基础上,根据开发人员修改的产品输出信息对之做相应的修改。比较好的可维护性方法是,每次只检查选定的产品的某些特定输出,而不是对比所有的结果输出。产品的接口变动也会导致原来的测试无法执行或者执行失败。对于 GUI 测试,这是一个更大的挑战。研究由于产品接口变化引起的相关测试修改,并研究使测试修改量最小的方法,测试中采用库是解决问题的方法。当产品发生变化的时候,只需要修改相关的库,保证测试与产品的变动同步修改即可。
完整性: 当自动化测试执行后,报告测试执行通过,可以断定这是真的吗?这就是我称之为测试套的完整性。在前面的故事中,当没有对自动化测试完整性给予应有的关注的时候,发生了富有喜剧性的情况。我们应该在多大程度上相信自动化化测试执行结果?自动化测试执行中的误报告警是很大的问题。测试人员特别讨厌由于测试脚本自身的问题或者是
测试环境的问题导致测试执行失败,但是,对于自动化测试误报告警的情况,大家往往无能为力。你期望自己设计的测试对 BUG 很敏感、有效,当测试发现异常的时候,能够报告测试执行失败。有的
测试框架支持对特殊测试结果的分类方法,分类包括 PASS , FAIL 和第三种测试结果 NOTRUN 或者 UNRESOLVED 。无论你怎么称呼第三种测试结果,它很好的说明了由于某些测试失败导致其他
测试用例无法执行而并非执行失败的情况。得到正确的测试结果是自动化测试完整性定义的一部分,另一部分是能够确认执行成功的测试确确实实已经执行过了。我曾经在一个测试队列中发现一个 BUG ,这个 BUG 引起测试队列中部分测试用例被跳过,没有执行。当测试队列运行完毕后,没有上报任何错误,我不得不通过走读代码的方式发现了这个 BUG 。如果,我没有关注到这个 BUG ,那么可能在认识到自动化测试已经出现问题之前,还在长时间运行部分测试用例。
独立性: 采用自动化方法不可能达到和
手工测试同样的效果。当写手工测试执行的规程时候,通常假定测试执行将会由一个有头脑、善于思考、具有观察力的测试人员完成的。如果采用自动化,测试执行是由一台不会说话的计算机完成的,你必须告诉计算机什么样的情况下测试执行是失败的,并且需要告诉计算机如何恢复测试场景才能保证测试套可以顺利执行。自动化测试可以作为测试套的一部分或者作为独立的测试执行。测试都需要建立自己所需要的测试执行环境,因此,保证测试执行的独立性是唯一的好方法。手工回归测试通常都相关的指导文档,以便一个接着一个有序地完成测试执行,每个测试执行可以利用前一个测试执行创建的对象或数据记录。手工测试人员可以清楚地把握整个
测试过程。在自动化测试中采用上述方法是经常犯的错误,这个错误源于 “ 多米诺骨牌 ” 测试套,当一个测试执行失败,会导致后续一系列测试失败。更糟糕的是,所有的测试关联紧密,无法独立的运行。并且,这使得在自动化测试中分析合法的执行失败结果也困难重重。当出现这种情况后,人们首先开始怀疑自动化测试的价值。自动化测试的独立性要求在自动化测试中增加重复和冗余设计。具有独立性的测试对发现的
缺陷的分析有很好的支持。以这种方式设计自动化测试好像是一种低效率的方式,不过,重要的是在不牺牲测试的
可靠性的前提下保证测试的独立性,如果测试可以在无需人看守情况下运行,那么测试的执行效率就不是大问题了。
可重复性: 自动化测试有时能够发现问题,有时候又无法发现问题,对这种情况实在没有什么好的的处理办法。因此,需要保证每次测试执行的时候,测试是以同样的方式工作。这意味着不要采用随机数据,在通用语言库中构造的随机数据经常隐藏初始化过程,使用这些数据会导致测试每次都以不同的方式执行,这样,对测试执行的失败结果分析是很让人沮丧的。这里有两个使用随机数据发生器的的方法可以避免上述情况。一种方法是使用常量初始化随机数据发生器。如果你打算生成不同种类的测试,需要在可预测,并且可控制的情况下建立不同类型的随机数据发生器。另外一个办法是提前在文件中或
数据库中建立生成随机测试数据,然后在测试过程中使用这些数据。这样做看起来似乎很好,但是我却曾经看到过太多违反规则的做法。下面我来解释到底看到了什么。当手动执行测试的时候,往往匆匆忙忙整理文件名和测试数据记录。当对这些测试采用自动化
测试方法,该做哪些工作呢?办法之一是,可以为测试中使用的数据记录给固定的命名。如果这些数据记录在测试完成后还要继续使用,那么就需要制定命名规则,避免在不同的测试中命名冲突,采用命名规则是一种很好的方法。然而,我曾经看到有些测试只是随机的命名数据记录,很不幸,事实证明采用这种随机命名的方式不可避免的导致命名冲突,并且影响测试的可重复性。很显然,自动化工程师低估了命名冲突的可能性。下面的情况我遇到过两次,测试数据记录的名字中包含四个随机产生的数字,经过简单的推算如果我们采用这种命名方式的时候,如果测试使用了 46 条记录,会存在 10% 冲突的可能性,如果使用 118 条记录,冲突的几率会达到 50% 。我认为测试当中使用这种随机命名是出于偷懒的想法 —— 避免再次测试之前写代码清除老的数据记录,这样引入了问题,损害了测试的可靠性和测试的完整性。
测试库: 自动化测试的一个通用策略是开发可以在不同测试中应用的测试函数库。我曾经看到过很多测试函数库,自己也写了一些。当要求测试不受被测试产品接口变动影响的时候,采用测试库方法是非常有效的。不过,根据我的观察测试库已经使用的太多了,已经被滥用了,并且测试库往往设计的不好,没有相关的文档支撑,因此,使用测试库的测试往往很难开展。当发现问题的时候,往往不知道是测试库自身的问题,还是测试库的使用问题。由于测试库往往很复杂,即便在发现测试库存在问题,相关的维护人员也很不愿意去修改问题。通过前文中的论述,可以得出结论,在一开始就应该保证测试库设计良好。但是,实际情况是
测试自动化往往没有花费更多的精力去保证一个优良设计的测试库。我曾经看到有些测试库中的功能根本不再使用了或仅仅使用一次。这与
极限编程原则保持一致,即 " 你将不需要它 " 。这会导致在测试用例之间的代码出现一些重复,我发现微小的变化可能仍然存在,很难与测试库功能协调。你可能打算对测试用例作修改,采用源代码的方式比采用库的方式更容易修改。如果有几个测试,他们有某些共同的操作,我使用剪切和粘贴的方式去复制代码,有的人认为我采用的方法不可理喻。这允许我根据需要修改通用代码,我不必一开始尝试和猜测如何重用代码。我认为我的测试是很容易读懂的,因为阅读者不必关心任何测试库的语法。这种办法的优势是很容易理解测试,并且很方便扩展测试套。在开发
软件测试项目的时候,大多数
程序员找到与他们打算实现功能类似的源代码,并对源代码做修改,而不是从头开始写代码。同样,在写测试套的过程中可以采用上述方法,这也是代码开发方式所鼓励的方法。我比较喜欢写一些规模比较小的测试库,这些库可以被反复的使用。测试库的开发需要在概念阶段充分定义,并且文档化,从始至终都应该保持。我会对测试库作充分的测试后,才在测试中使用这些测试库。采用测试库是对所有面临的情况作权衡的。千万不要打算写一个大而全的测试库,不要希望有朝一日测试人员会利用你的测试库完成大量的测试,这一天恐怕永远不会到来。
数据驱动测试: 把测试数据写入到简单表格中,这种测试技术得到了越来越广泛的应用,这种方法被称为表驱动( table-driven ),数据驱动 (data-driven) 或者 “ 第三代 ” 自动化测试( "third generation" automation )。这需要写一个解析器,用来解释表格中的数据,并执行测试。该测试架构的最主要的好处是,它允许把测试内容写在具有一定格式的表格中,这样方便数据设计和数据的检视。如果测试组中有缺少编程经验的业务专家参与测试,采用数据驱动测试方法是很合适的。数据驱动测试的解析器主要是由测试库和上层的少量
开发语言写成的代码组成的,所以,上面关于测试库的说明放在这里是同样合适的。在针对上面提到的少量代码的设计、开发、测试的工作,还存在各种困难。代码所采用的编程语言是不断发展的。也许,测试人员认为他们需要把第一部分测试的输出作为第二部分测试的输入,这样,加入了新的变量。接下来,也许有人需要让测试中的某个环节运行一百次,这样加入一个循环。你可以采用其他语言,不过,如果你预料到会面临上述情况的时候,那么做好采用一些能够通过公开的渠道获取的编程语言,比如 Perl,Python 或者 TCL ,这样比设计你自己的语言要快的多。