现代软件工程讲义 5.1 软件的质量保证 (QA) 和测试 (Test)
在一个软件团队里, 不同的人有不同程度的投入, 我们在 猪,鸡和鹦鹉 的故事里已经说明了. 不同的人还要在团队中担负不同的任务:
开发人员 (大部分内容在: 现代软件工程讲义 2 工程师的能力评估和发展)
项目经理 ( 内容在这里)
测试人员 ( 本篇博客 )
团队中的管理人员/PM 负责分析市场, 设想功能, 定义用户到底要什么 – Why & What.
团队中的开发人员/Dev 负责实现功能, 搞清楚怎么才能满足用户的需求 – How.
团队中的测试人员/QA 搞清楚我们的软件是否满足了用户的需求 – Whether.
最后所有成员一块决定产品是否能发布, 什么时候能发布 – When.
测试/Test 和 质量保障/QA (Quality Assurance) 这两个词经常混用, 这两个概念是完全同等, 还是只是有一部分交集, 还是是另一个的真子集, 众说纷纭, 在各自的语境中, 都有意义。对于《现代软件工程》的语境, 我们这样规定:
QA: 运用各种手段, 在软件工程的各个阶段确保软件的质量能帮助软件团队实现目标。
Test (测试) : 特指验证代码的行为是否符合功能规格说明书 (spec) 的规定。
在这样的定义下, Test只是 QA 工作的一部分。
对测试工作的种种误解
一些人对“测试”这一工作还有很多误, 解例如下面的似是而非的观点:
1) 测试在项目的最后进行就可以了。
这是远远不够的。当你在项目后期发现了问题,问题的根源往往是项目早期的一些决定和设计,这时候,再要对其进行修改就比较困难了。这要求测试人员从项目开始就要积极介入,从源头防止问题的发生。有人会说,我是一个小小的测试人员,项目开始的时候我能做什么?这就是小小测试人员努力的方向。
2) 测试就得根据规格说明书(spec)来测,所以是很机械的。
那不一定,即使你的软件产品功能100% 符合spec 的要求,但是用户也可能非常恨你的软件。这时,测试人员就没有尽到责任,因为测试人员要从用户的角度出发,测试软件。
3) 测试人员当然也写代码,但是质量不一定要很高。
开发人员的代码没写好,可以依赖于测试人员来发现问题。但是如果测试人员的代码没写好,我们依赖谁来测试和改错呢?这就要求我们测试人员的代码质量特别高,因为我们是最后一道防线,如果我们的代码和测试工作有漏洞,那么Bug就会跑到用户那里去。
4) 测试只是被动地接受别人的产出, 然后开始自己的工作, 比较被动, 不能发挥创造性。
也许狭义定义下的 测试用例 是要等开发人员的代码, 然后开始测试。 但是整个质量保证工作(QA)需要前瞻性, 主动性, 创造性的工作。Weinberg 说过: “也许没有任何一项测试技术比前瞻性更有价值” (Probably no single testing technique is of more value than foresight.) [1]
各种测试方法
(注意到上图的黑箱子和白箱子了么? 它们里面装的都是测试的宝贝)
[下面的解释大部分来自于 《移山之道》 ]
统一思想要从基本名词解释开始。
1.Bug:缺陷软件的缺陷
Bug可以分为这三个组成部分:症状(Symptom)、程序错误(Fault)、根本原因(Root cause)。
(1)Symptom:即从用户的角度看,软件出了什么问题。
例如,在输入(3 2 1 1)的时候,程序错误退出。
(2)Fault:即从代码的角度看,代码的什么错误导致了软件的问题。
例如,代码在输入为某种情况下访问了非法的内存地址——0X0000000C。
(3)Root Cause:错误根源,即导致代码错误的根本原因。
例如,代码对于id1==id2的情况没有做正确判断,从而引用了未赋初值的变量,产生了以上的情况。
以下是一个完整的例子。
(1)Symptom:用户报告,一个Windows应用程序有时会在启动时报错,程序不能运行。
(2)Fault:有时候一个子窗口的handle为空,导致程序访问了非法内存地址,此为代码错误。
(3)RootCause:代码并没有确保创建子窗口(在CreateSubWindow()内部才做)发生在调用子窗口之前(在OnDraw()时调用),因此子窗口的变量有时会在访问时为空,导致上面提到的代码错误。
2.Test Case:测试用例
测试用例描述了一个完整的测试过程,包括测试环境、输入、期望的结果等。
3.Test Suite:测试用例集合
即一组相关的测试用例。
提示:Suite发音念作“sweet”,不是念作“suit”,一大半的学生都念错。
测试设计有两类方法:Black box(黑箱)、White box(白箱)。
这是每个接触过软件测试的人都会给出的答案。但这只是整个软件测试的入门知识。可以跳过去,直接讨论下面的内容。
问:我在网上看到有人争论黑箱测试和白箱测试哪一个是另一个的基础,还有哪一个更难,哪一个更有前途,等等。据说河曲数码在搞“灰箱测试”,是不是更高级?能不能简单讲一讲?
阿超:大家都有这些问题么?
杂曰:[略去对此问题热烈的争论500字]
阿超:听了大家的争论,看来我们的确得花不少时间统一认识。
所谓黑箱/白箱,是指软件测试设计的方法,不是软件测试的方法!注意“设计”二字。
黑箱:在设计测试的过程中,把软件系统当作一个“黑箱”,无法了解或使用系统的内部结构及知识。一个更准确的说法是“Behavioral Test Design”,从软件的行为,而不是内部结构出发来设计测试。
白箱:在设计测试的过程中,设计者可以“看到”软件系统的内部结构,并且使用软件的内部知识来指导测试数据及方法的选择。“白箱”并不是一个精确的说法,因为把箱子涂成白色,同样也看不见箱子里的东西。有人建议用“玻璃箱”来表示。
在实际工作中,我们不应画地为牢,严格只用某一种方法来设计测试方法。在实际的测试中,当然是对系统了解得越多越好。所谓“灰箱”的提法,正是这一反映。有些测试专家甚至希望我们忘记全部的“箱子”和它们的颜色。
问:如果我是一个开发者,我能做“黑箱”么?
答:并不是要禁止懂得程序内部结构的人员来进行黑箱测试设计,只不过是在设计时有意不考虑软件的内部结构。例如:在测试程序内部基本模块的时候(单元测试),通常要求由对程序结构非常了解的程序员来设计,这是因为内部模块的“行为”和程序的外部功能并没有直接的关系,而且内部基本模块的“行为”通常没有明确的定义。另一个例子是“易用性测试”,在设计此类测试的时候,没必要纠缠于程序的内部结构,而是着重于软件的界面和行为。但是软件易用性测试也需要很多专业知识。这也从一个侧面表明“黑箱”和“白箱”没有简单的高低之分。
一旦测试用例写出来之后,大可以忘了它们是从哪种颜色的箱子里出来的,用它就可以了。
问:有人说“黑箱”,有人说“黑盒”,到底是“箱子”还是“盒子”?
答:在网上搜索了一下,“黑箱测试”有超过100万个记录,“黑盒测试”只有70多万。所以“箱子”赢了。
问:但是我听九条说他刚进公司实习的时候只能做“黑箱测试”,这是什么意思?
九条:我刚到公司实习的时候,两眼一摸黑,看到啥都是“黑箱”,即使测试用例是由懂得程序结构的开发人员写出来的,我还是只会机械地运行。我是知其然,不知其所以然,箱子当然是黑的。后来看得多了,学了一些东西,能够了解程序的结构和算法,箱子的颜色就变浅了,好像能看到箱子里的东西一样。
以下的测试术语主要是测试软件的功能。在表7-1所列的测试中,测试的范围由小到大,测试者也由内到外——从程序开发人员(单元测试)到测试人员,到一般用户(Alpha/Beta测试)。
表7-1 功能测试分类
测试名称 |
测试内容 |
Unit Test |
单元测试——在最低的功能/参数上验证程序的正确性 |
Functional Test |
功能测试——验证模块的功能 |
Integration Test |
集成测试——验证几个互相有依赖关系的模块的功能 |
Scenario Test |
场景测试——验证几个模块是否能够完成一个用户场景 |
测试名称 |
测试内容 |
System Test |
系统测试——对于整个系统功能的测试 |
Alpha/Beta Test |
外部软件测试人员(Alpha/Beta测试员)在实际用户环境中对软件进行全面的测试 |
2.非功能测试
一个软件除了基本功能之外,还有很多功能之外的特性,这些叫“non-functional requirement”,或者“quality of service requirement”——服务质量需求。没有软件的功能,这些特性都无从表现出来,因此,我们要在软件开发的适当阶段——基本功能完成后做这些测试。
如表7-2所示:
表7-2 非功能测试
测试名称 |
测试内容 |
Stress/load test |
测试软件在负载情况下能否正常工作 |
Performance test |
测试软件的效能 |
Accessibility test |
可访问性测试——测试软件是否向残疾用户提供足够的辅助功能 |
Localization/Globalization Test |
本地化/全球化测试 |
Compatibility Test |
兼容性测试 |
Configuration Test |
配置测试——测试软件在各种配置下能否正常工作 |
Usability Test |
易用性测试——测试软件是否好用 |
Security Test |
软件安全性测试 |
在开发软件的过程中,不少测试起着“烽火台”的作用,它们告诉我们软件开发的流程是否顺畅,这些测试如表7-3所示:
表7-3 烽火台
测试名称 |
测试内容 |
Smoke Test |
“冒烟”——如果测试不通过,则不能进行下一步工作 |
Build Verification Test |
验证构建是否通过基本测试 |
Acceptance Test |
验收测试,为了全面考核某方面功能/特性而做的测试 |
另一些测试名称,则是说明不同的测试方法,如表7-4所示:
表7-4 不同测试方法
测试名称 |
测试内容 |
Regression Test |
“回归”测试——对一个新的版本,重新运行以往的测试用例,看看新版本和已知的版本相比是否有“退化”(Regression) |
Ad hoc (Exploratory) Test |
随机进行的、探索性的测试 |
Bug Bash |
Bug大扫荡——全体成员参加的找“小强”活动 |
Buddy Test |
伙伴测试——测试人员为开发人员(伙伴)的特定模块作的测试 |
二柱:我们也试过用单元测试来保证质量,要求每人都要写,在签入代码前必须通过单元测试。但是搞了几个星期就不了了之。
大家七嘴八舌地列举了单元测试的问题:
◆ 有时单元测试报了错,再运行一次就好了,后来大家就不想花时间改错,多运行几次,有一次通过就行了;
◆ 单元测试中好多错都和环境有关,在别人的机器都运行不成功;
◆ 花在单元测试上的时间要比写代码的时间还多,提高代码覆盖率到90%以上太难了;
◆ 单元测试中我们还要测试效能和压力,花了很多时间;
◆ 我们都这么费劲地测了,那还要测试人员干什么?
阿超:看来问题还不少,我们留到后面再谈(见后面“单元测试”的具体描述)。
前面单元测试中提到代码覆盖率,简单来说代码被执行过,就是“覆盖过”,如果一段程序运行了一组测试用例之后,100%的代码被执行了,那么是否就说明再也不用写新的测试用例了呢?
答案是否定的。
(1)不同代码是否执行,有很多组合,一行代码被执行过,没有问题,并不表明这一行程序在所有可能条件的组合下都能正确无误地运行。
(2)代码覆盖不能测出还没有写的代码(缺少的逻辑)导致的错误。比如:
a. 没有检查过程调用的返回值;
b. 没有释放资源。
(3)代码覆盖不能测出效能问题。
(4)代码覆盖不能测出时序问题,由时序导致的程序错误(例如:线程之间的同步)。
(5)代码中和用户界面相关的功能不能简单地以代码覆盖率来衡量优劣。
望文生义,构建验证测试是指在一个构建完成之后,团队自动运行的一套验证系统基本功能的测试。在大多数情况下,这一套系统都是在自动构建成功后自动运行的,有些情况下也会手工运行,但是由于构建是自动生成的,我们也要努力让BVT自动运行。
问:一个系统有这么多功能点,什么是基本的功能,什么不是基本的功能?
答:第一,必须能安装;第二,必须能够实现一组核心场景。例如,对于字处理软件来说,必须能打开/编辑/保存一个文档文件,但是一些高级功能,如文本自动纠错,则不在其中;又如,对于网站系统,用户可以注册/上传/下载信息,但是一些高级功能,如删除用户,列出用户参与的所有讨论,则不在其中。
在运行BVT之前,可以运行所有的单元测试,这样可以保证系统的单元测试和程序员的单元测试版本一致。在不少情况下,开发人员修改了程序和单元测试,但是忘了把修改过的单元测试也同时签入源代码库中。
通过BVT的构建可以称为可测(Self-test),意思是说团队可以用这一版本进行各种测试,因为它的基本功能都是可用的。通不过BVT的构建称为“不可测”(Self-hosed)。
如果构建测试不能通过,那么自动测试框架会自动对每一个失败的测试产生一个Bug(小强)。一般的做法下这些小强都有最高优先级,开发人员要首先修改这些小强。大家知道维持每日构建,并产生一个可测的版本是软件开发过程中质量控制的基础。对于导致问题的小强,我们该怎么办?答案是——
(1)找到导致失败的原因,如果原因很简单,程序员可以马上修改,然后直接提交。
(2)找到导致失败的原因的修改集,把此修改集剔出此版本(程序员必须修改好后再重新提交到源代码库中)。
(3)程序员必须在下一个构建开始前把此小强修理好。
方法(1)和(2)都可以使今天的构建成为“可测”,但是有时各方面的修改互相依赖,不能在短时间内解决所有问题,那就只能采用第三种方法了。
问:有人提到一种“Smoke Test”,冒烟测试,是怎么回事?
答:事实上这是一种基本验证测试,据说是从硬件设计行业流传过来的说法。当年设计电路板的时候,很多情况下,新的电路板一插上电源就冒起白烟,烧坏了,如果插上电源后没有冒烟,那就是通过了“冒烟测试”,可以进一步测试电路板的功能了。我们正在讨论的BVT也是一种冒烟测试。
测试团队拿到一个“可测”等级的构建,他们就会按照测试计划,测试各自负责的模块和功能,这个过程可能会产生总共10来个Bug,也可能产生100个以上的Bug,那么如何保证我们有效地测试软件,同时我们在这一步怎样衡量构建的质量?
在MSF敏捷模式中,我们建议还是采用场景来规划测试工作。
在“基本场景”的基础上,把所有系统理论上目前支持的场景都列出来,然后按功能分类测试,如果测试成功,就在此场景中标明“成功”,否则,就标明“失败”,并且把失败的情况用一个(或几个)“小强”/Bug来表示。
当所有的测试人员完成对场景的测试,我们自然地就得出了表7-5。
表7-5 场景测试报告
场景ID |
场景名 |
测试结果 |
Bug/小强ID |
3024 |
用户登录 |
成功 |
|
3026 |
用户按价格排序 |
失败 |
5032 |
3027 |
用户按名字搜索 |
失败 |
5033 |
…… |
…… |
…… |
…… |
这样就能很快地报告“功能测试56%通过”等。如果所有场景都能通过,(有些情况下可以把此标准从100%降低到90%左右)则这个构建的质量是“可用”,意味着这一个版本可以给用户使用。在这种情况下,客户、合作伙伴可以得到这样的版本,这也是所谓“技术预览版”或“社区预览版”的由来。
但是,有一个重要的问题要大家注意:“可用”,并不是指软件的所有功能都没有问题,而是指在目前的用户场景中,按照场景的要求进行操作,都能得到预期的效果,注意以下两种情况:
(1)在目前还没有定义的用户场景中,程序质量如何,还未得而知。
例如:场景中没有考虑到多种语言设置。
(2)不按照场景的要求进行的操作,结果如何,还未得而知。
如:在某一场景中,场景规定用户可以在最后付款前取消操作,回到上一步,如果一个测试人员发现在反复提交/取消同一访问多次后,网页出现问题,这并不能说明用户场景失败,当然对于这个极端的Bug,也必须找出原因并在适当的时间改正。
这种测试有时也被称为验收测试“Acceptance Test”,因为如果构建通过了这样的测试,这一个构建就被测试团队“接受了”。同时,还有对系统各个方面进行的“验收”测试,如系统的全球化验收测试,或者针对某一语言环境、某一个平台做的测试。
“Ad hoc”原意是指“特定的,一次性的”。这样的测试也可以叫Exploratory Test。
什么叫“特定”测试?或者“探索式”的测试?
就是为了某一个特定目的进行的测试,就这一次,以后一般也不会重复测试。在软件工程的实践中,“Ad hoc”大部分是指随机进行的、探索性的测试。
比如:测试人员阿毛拿到了一个新的构建,按计划是进行模块A的功能测试,但是他灵机一动,想看看另一个功能B做得如何,或者想看看模块A在某种边界条件下会出现什么问题,于是他就“Ad hoc”一把,居然在这一功能模块中发现了不少小强。
“Ad hoc”也意味着测试是尝试性的,“我来试试,在这个对话框中一通乱按,然后随意改变窗口大小,看看会出什么问题……”,如果没问题,那么以后也不会再这么做了。
一般情况下,测试人员不会花很多时间进行特定测试,但是在一些缺乏管理的团队中,很多时候测试人员不知道自己此时应该做什么,只好做一些看似“Ad hoc”的测试,比如随机测试各个功能的各个方面。这些测试理论上都应该由测试管理人员规划好属于各个功能模块的测试用例。
在一个团队中,“Ad hoc”太多是一个管理不好的标志,因为“Ad hoc”是指那些一时想到要做,但是以后也没有计划经常重复的测试计划。
问:我听说有人是“Ad hoc”测试的高手,这是什么意思?
答:有很多测试人员会按部就班地进行测试,但是还有一些人头脑比较灵活,喜欢另辟蹊径,测试一些一般人不会想到的场景,这些人往往会发现更多的小强。开发人员对这样的“Ad hoc”高手是又爱又恨。
问:看问题要分两方面,有些“Ad hoc”发现的小强在正常使用软件中几乎不会出现,我们要不要花时间“Ad hoc”?
答:现在一些成功的通用软件的用户以百万计,按部就班的测试计划很难包括很多实际的场景,这时,“Ad hoc”测试能够发现重要的问题;另外一些风险很大的领域,例如安全性,一旦出了问题,威胁就会相当大,这时要多鼓励一些“Ad hoc”测试,以弥补普通测试的不足。从这个意义上说,“Ad hoc”测试可以用来衡量当前测试用例的完备性,如果你探索了半天,都没有发现什么在现有测试用例之外的问题,那就说明现有的测试用例是比较完备的。
“Ad hoc”测试的测试流程是不可重复的,因为它的测试都是“特定”测试,没法重复。由于这一原因,“Ad hoc”测试不能自动化,就这一点而言,还达不到CMM的第二级——可重复级。
作为管理人员来说,如果太多的小强是在“Ad hoc”出来的,那我们就要看看测试计划是否基于实际的场景,开发人员的代码逻辑是否完善,等等。同时,要善于把看似“Ad hoc”的测试用例抽象出来,囊括到以后的测试计划中。
问:我听说不少关于Regression Test的介绍,但是它到底是怎么“回归”法?回归到哪里去?我还是没搞懂。
答:Regress的英语定义是:return to a worse or less developed state。是倒退、退化、退步的意思。
在软件项目中,如果一个模块或功能以前是正常工作的,但是在一个新的构建中出了问题,那这个模块就出现了一个“退步”(Regression),从正常工作的稳定状态退化到不正常工作的不稳定状态。
在一个模块的功能逐步完成的同时,与此功能有关的测试用例也同样在完善中。一旦有关的测试用例通过,我们就得到了此模块的功能基准(Baseline)。
假如,在3.1.5版本,模块A的测试用例125是通过的,但是测试人员发现在新的版本3.1.6,这个测试用例却失败了,这就是一个“倒退”。在新版本上运行所有已通过的测试用例以验证有没有“退化”情况发生,这个过程就是一个“Regression Test”。如果这样的“倒退”是由于模块的功能发生了正常变化(由于设计变更的原因)引起的,那么测试用例的基准就要修改,以便和新的功能保持一致。
针对一个Bug Fix(拖鞋),我们也要作Regression Test。
(1)验证新的代码的确把缺陷改正了。
(2)同时要验证新的代码没有把模块的现有功能破坏,没有Regression。
所以对于“回归测试”中的“回归”,我们可以理解为“回归到以前不正常的状态”。
回归测试最好要自动化,因为这样就可以对于每一个构建快速运行所有回归测试,以保证尽早发现问题。在微软的实践中,在一个项目的最后稳定阶段,所有人都要参加测试,以验证所有已经修复过的Bug的确得到了修复,并且没有在最后一个版本中“复发”,这是一个大规模的、全面的“回归测试”。
在软件开发的一定阶段,我们要对一个软件进行全面和系统的测试,以保证软件的各个模块都能共同工作,在各方面都能满足用户的要求。这时的测试叫系统/集成测试。
问:有一种测试叫Scenario Test,是什么意思?
答:就是以场景为驱动的集成测试,关于“场景”,大家可以看专门的介绍。这一方法的核心思想是:当用户使用一个软件的时候,他/她并不会独立使用各个模块,而是把软件作为一个整体来使用的。我们在做场景测试的时候,就需要考虑在现实环境中用户使用软件的流程是怎样的,然后模拟这个流程,看看软件能不能达到用户的需求。这样,能使软件符合用户使用的实际需求。
以一个数字照片编辑软件为例,这个软件的各个模块都是独立开发的,可是用户有一定的典型流程,如果这个流程走得不好,哪怕某个模块的质量再高,用户也不会满意。用户的典型流程是:
(1)把照相机的储存卡插入电脑。
(2)程序会弹出窗口提示用户导入照片。
(3)用户根据提示导入照片。
(4)对照片进行快速编辑。
a. 调整颜色;
b. 调整亮度,对比度;
c. 修改红眼。
(5)选择其中几幅照片,用E-mail发送。
这里面哪一步出了问题,都会影响用户对这一产品的使用。如果这里面各个模块的用户界面不一致(即使是“确认”和“取消”按钮的次序不同),用户使用起来也会很不方便。这些问题都是在单独模块的测试中不容易发现的。
问:什么时候做集成测试?是不是越快越好?
答:原则上是当一个模块稳定的时候,就可以把它集成到系统中,和整个系统一起进行测试。在模块本身稳定之前就提早做集成测试,可能会报告出很多Bug,但是这些由于提早测试而发现的Bug有点像汽车司机在等待绿灯时不耐烦而拼命地按喇叭——有点像噪音。我们还是要等到适当的时机再开始集成测试。
问:但是开发人员也想早日发现并修复所有的Bug,软件工程的目标不就是要早发现并修正问题么?总是要等待,听起来好像没有多少效率。
答:对,这就要提到在微软内部流行的另一种测试——Buddy Test伙伴测试。
如上所述,在开发一个复杂系统的过程中,当一个新的模块(或者旧模块的新版本)加入系统中时,往往会出现下列情况。
(1)导致整个系统稳定性下降。不光影响自己的模块,更麻烦的是阻碍团队其他人员的工作。
(2)产生很多Bug。这些Bug都要被输入到数据库中,经过层层会诊(Triage),然后交给开发人员,然后再经历一系列Bug的旅行,才能最后修复,这样成本变得很高。
如何改进?一个方法当然是写好单元测试,或者运用重构技术以保证稳定性等,我们要讲的伙伴测试是指开发人员可以找一个测试人员作为伙伴(Buddy),在新代码签入之前,开发人员做一个私人构建(Private Build),其中包括了新的模块,测试人员在本地做必要的回归/功能/集成/探索测试,发现问题直接和开发人员沟通。通过伙伴测试把重大问题都解决了之后,开发人员再正式签入代码。
在项目的后期,签入代码的门槛变得越来越高,大部分团队都要求Bug fix必须得到了伙伴测试的验证后才能签入到代码库中。
在开发软件的过程中,开发团队希望让用户直接接触到最新版本的软件,以便从用户那里收集反馈,这时开发团队会在开发过程中让特定的用户(Alpha/Beta用户)使用正处于开发过程中的版本,用户会通过特定的反馈渠道(E-mail、BBS)与开发者讨论使用中发现的问题,等等。这种做法成功地让部分用户心甘情愿地替开发团队测试产品并提出反馈。
从惯例上说,Alpha Test一般指在团队之外,公司内部进行的测试;Beta Test指把软件交给公司外部的用户进行测试,与之对应地,软件就有Alpha、Beta1、Beta2版本。在网络普及之前,做Beta Test是很花费人力物力的事情,现在由于网络的传播速度很快,与外部用户的联系渠道很畅通,很多外部用户都想先睹为快。因此最近开发团队增加了反馈的密度,不必再局限于Alpha或者Beta发布,而是不断地把一些中间版本发布出去以收集反馈,美其名曰“技术预览版本”(Technical Preview Release)或“社区预览版本”(Community Preview Release)。
小燕:作为测试人员,我们是不是也要做易用性测试?
答:测试人员,以及其他的团队成员都可以对软件的可用性提出意见,包括以Bug的形式放在TFS中。软件的可用性并不神秘,就是让软件更好用,让用户更有效地完成工作。
但是我觉得“易用性测试”似乎更多地用来描述一套测试软件可用性的过程,这个过程一般不是由测试人员来主导的,而是由对软件设计及对可用性有很多研究的“可用性设计师”来实行。网络上有许多关于这方面的文章,大家可以参考。
为了弄清软件的可用性,并了解用户的需求,移山公司的员工特地进行了一个易用性测试——
小飞学了很酷的Web“WPF/我佩服”界面技术,然后做了一个小游戏——3D挖雷。大家用了之后,都觉得不错,用鼠标单击/双击,左键/右键都可以进行各种不同的操作。于是他们迫不及待地找一个“典型用户”来做易用性测试。
王屋村的村民,石头他爹刚好路过,他被移山公司的小伙子们拉了进来,成为第一个“典型用户”。
大家七嘴八舌地介绍了游戏的功能,就让石头他爹试一试。石头他爹看到鼠标,说,这个怎么和俺家里的不一样?小飞说:这是光电鼠标,好用得很!
三分钟过去了,游戏还没有开始;
五分钟,十分钟过去了,游戏还是没有进展。
阿超走过去看看到底怎么回事——
原来,石头他爹手指不灵活,在按鼠标的时候鼠标的位置会稍稍移动,导致程序无法捕捉鼠标双击事件。问题是在小飞设计的游戏中,鼠标单击、双击都可以,而且是不同的功能。
同时,有些功能还只能够通过右键弹出菜单来执行。
石头他爹看起来很迷惑。这时,小飞说:左键/右键/单击/双击都可以。
从此之后,石头他爹对每一个操作都问:是按左键还是按右键?是按一下还是两下?
小飞露出了“faint”的表情。
半个小时后,大家送走了石头他爹,同时送他一个鼠标垫作为礼物。
阿超:(目送石头他爹的背影)幸好……
小飞:幸好啥?
阿超:幸好你还没有介绍你那超级功能,要按住Ctrl键,同时拖动鼠标才能使用。否则我们还要花半个小时陪石头他爹一起学习玩这个游戏。
问:我们已经讲了太多的测试了,好像微软还有一个叫“Bug Bash”的活动,是啥意思?
答:Bug Bash,或者叫Bug Hunt,简而言之,就是大家一起来找小强的活动——小强大扫荡。一般是安排出一段时间(一天),所有测试人员(有时也加入其他角色)放下手里的事情,专心找某种类型的小强。然后结束时,统计并奖励找得最多和最厉害的小强的员工。
问:这是不是可以看做是“全体动员Ad hoc”?
答:一般情况下是的,但是并不是全体人员用键盘鼠标一通乱敲乱点就可以搞定,大扫荡的内容也应该事先安排好。
这种活动,如果运用得好,会带来这样的功效:
◆ 鼓励大家做探索试的测试,开阔思路;
◆ 鼓励测试队伍学习并应用新的测试方法,例如在做完“软件安全性测试”培训后,立马做一个针对“安全性”的小强大扫荡,或者为“全球化/本地化测试”做一个小强大扫荡也是很常见的;
◆ 找到很多小强,让开发人员忙一阵子。
当然,小强大扫荡也有一些副作用:
◆ 扰乱正常的测试工作;
◆ 如果过分重视奖励,会导致一些数量至上,滥竽充数的做法。
因此,两个需要提醒的细节是:
(1)一定要让“小强大扫荡”有明确的目标、明了的技术支持。
(2)一定要让表现突出的人介绍经验,让别人学习。
要记住,最好的测试,是能够防止小强的出现。
阿毛:超总,我的脑袋好像装不下了!听了这么多,我感觉像是身上扛着十八般兵器,它们互相碰撞,叮叮当当。我累得半死,但是不知道什么时候,对哪一种敌人用哪一种兵器,能不能总结一下!
阿超:好,我们用软件开发的生命周期来说明一下不同的测试在不同阶段的使用。
1.远景和计划阶段
此时,测试只是处于计划阶段,我们要讨论测试计划和测试设计说明书,同时要收集用户对于软件非功能性的需求,如效能、可用性、国际化等。一些“小强大扫荡”的类型也可以在这个时候初步安排。
2.开发阶段。
开发人员要写单元测试,测试人员要写BVT。
对于每一个成功的构建,测试人员要运行功能测试/场景测试,同时建立回归测试基准以便开始回归测试。各类测试人员要进行探索式测试以求尽早发现问题。
随着软件功能的逐步完善,测试人员要进行集成测试。这时,团队可以开展对程序非功能性特性的测试,如效能/压力测试、国际化/本地化测试、安全性测试、可用性、适用性测试等。在这个时候,可以考虑分析各个模块的代码覆盖率,以增加测试的有效性。根据计划,各种类型的“小强大扫荡”会以适当的频率发生。
3.稳定阶段。
到了一个开发阶段的尾声,这时测试团队就可以依据以前制定的验收标准,对软件逐项进行验收测试。按照测试计划,各个方面的测试都会宣布“测试完成”——所有想到的测试都做了,所有问题都发现了。在此阶段,团队也可以把软件发布给外部进行Alpha/Beta测试。
这时,伙伴测试会用于保证新代码签入前能得到足够的检测。
一般情况下,测试队伍要把迄今为止发现的所有小强都重新试一遍,确保它们都在最后的版本中被清除了,没有一个“回归”出现。
4.发布阶段。
测试队伍要把尽可能多的测试用例自动化,并为下一个版本的测试工作做好准备。
这会在后面的章节中讨论。测试计划的模板在移山社区网站上有下载。
7.15.3 如果一个Bug在实际应用中根本不可能发生,这还是一个Bug么
看这里:http://www.testingcraft.com/Bug-in-forest.pdf。
另外,要知道这世界上有各种各样的用户,有些用户“亡软件之心不死”,IE和Windows的许多安全漏洞,都在这些用户的努力下被发现并且被利用了。很多人当初会说“缓冲区溢出?这是根本不会发生的事,用户怎么会在字符串后面加这么多乱七八糟的东西?!”。
7.15.4 Bug的数量和测试人员的工作效率有关么?和开发人员的工作绩效有关么
阿亨:当然有关!我们会在以后的实践中碰到这些问题。
阿超:有关,但是也不是太有关。一个极端的例子,如果一个开发人员写的模块没有任何Bug,那测试人员的工作效率如何衡量?我们以后再说。
7.15.5 有错不改
果冻:微软的产品经过这么多版本的不断完善,应该是把所有问题都搞定,“止于至善”了吧?
阿超:那也不一定,在非常有名的电子表格软件Excel中,就有这样一个Bug:Excel 的日期计算功能认为1900年是一个闰年,这是不对的,但是它愣是一直没有改正这个错误。
众人:真的?为什么屡教不改呢?
阿超:故事是这样的,当时这类电子表格软件的市场领头羊是Lotus 1-2-3这一款软件。它的日期计算功能有一个Bug,就是把1900 年当作闰年。这类软件在内部把日期保存为“从1900/1/1 到当前日期的天数”这样的一个整数。Excel 作为后来者,要支持 Lotus 1-2-3 的数据文件格式,这样才能正确处理别的软件产生的格式文件。这个错误就这么延续下来了,每一版本都有人报告,但是都没有改正。我们可以在Excel 中试试看:
在任意格子(cell)中输入“=DATE(1900,2,28)”,并且定义这个格子的格式为数字。大家可以看到数值变为:59。表明1900/2/28 是1900/1/1开始的第59天。
输入“=DATE(1900,2,29)”,可以看到 60! 这是一个不存在的日期!
输入“=DATE(1900,3,1)”,数值是61,事实上,这应该是60。从这一天开始的所有日期都错了一天。
果冻:还是可以抓住机遇,促成飞跃,在某一个版本彻底改好,不就是一个数字嘛。
阿超:改这个问题,技术上一点问题都没有。但是在现实中会出现下列问题:
(1)几乎所有现存文件的日期数据都要减少一天,所有依赖于日期的 Excel公式也要做检查和修改。这在现实生活中是很难办到的。
(2)Excel的日期问题解决了,但是其他软件还是有这个Bug,数据文件在不同软件中使用,就会有很头痛的兼容性问题。
总之,这个问题就这样一直留下来了。中间也有人想改过,你要注意看Excel 的 Options 设置,就会发现有这样一个设置——使用1904年开始的日期计算系统(use 1904 date system)(如图7-1所示),但是一般的用户谁没事在这里打一个勾?
图7-1
另: 关于闰年的bug 还有好些, 请看看这里: http://www.cnblogs.com/xinz/archive/2011/11/29/2267022.html
[1] Computer Programming Fundamentals, 2nd ed. (New York: McGraw-Hill, 1966) Herb Leeds, Gerald M Weinberg