【关键词】软件测试,黑盒测试
引言――不能不说的二个问题
软件测试中的“二八”原则
80%左右的错误在进行用户测试之前已经被发现,而在剩余20%左右的错误中,存在80%左右的显性错误,剩余20%左右的错误是较难发现的隐性错误。这条原则源自经济学的二八法则(注[ 1] )。所以,我并不认为自己从事了一项伟大的工作,但是必须承认做好了这项工作对于整个软件开发体系在用户心目中的意义巨大。
软件黑盒测试解决的问题
简单来说,黑盒测试所解决的问题主要在于以用户眼光验证软件的结果。白盒测试关注范围(控制结构),而黑盒测试更关注结果(即我们常说的所见即所得)。黑盒测试试图发现以下几类错误:
l 功能错误或遗漏
l 界面错误
l 数据结构或外部数据库访问错误
l 性能错误
l 初始化错误和终止错误
以下内容将会详细说明在软件黑盒测试中常见的各类错误。
正文――软件黑盒测试常见错误类型及说明
一、用户界面错误
软件是为了满足用户需求而诞生的产物[ 2] ,无论是操作系统、游戏软件还是其他类型的应用软件。黑盒测试的很大一部分工作集中在用户界面上,不需要深入研究其内在结构,而是“表面化[ 3] ”地使用软件,从输入输出的信息内容中寻找可能的错误和纰漏。总体上讲,用户对于软件的看法很大程度上依赖以下几点:
l 功能性(实现软件应具备的基本功能)
l 易用性(用户学习掌握该软件所耗费的时间及在具体业务流程上的简化)
l 执行速度(多数是启动速度,查询速度,刷新速度及响应时间等因素)
l 用户使用时产生错误的比率(在允许用户任意使用的情况下,越少越好)
l 用户满意度(这里指的是用户界面设计与功能设计的用户评价)
下面我们分开对该类型错误进行分析与描述。
1、功能性
如果出现了以下情况之一,可以认为程序可能存在功能性错误:程序可以完成的某些事进行得非常困难,笨拙(繁琐),令人迷惑甚至难以忍受。主要表现为以下几个方面:
1)过度功能性
将简单功能复杂化,这是设计上一个较常见的问题。尝试进行太多工作任务的系统将很难学习和掌握,而且容易忘记。它要求大量的文档(开发文档,帮助文档和屏幕)。如果功能模块间模块过于紧密,则发生关联错误的几率要提高不少。有时候,用户需要的只是简单功能,而不要让它过于膨胀成为一个“怪物”。
2)夸大的功能性印象
用户手册和营销传单不能使程序功能实现得更多,尤其是营销传单。记住,在用户手册中哪怕宁愿对功能略微轻描淡写也不能夸大其词(当然,我们并不希望这样,我们总是要对此如实地进行编写――这是我们的责任)。
3)对手头任务的不适当性
我们可以把它直观的理解为需求设计错误。对于任何一个项目,由于功能关键事项(就是常说的需求提炼)不存在、太有限(多数是因为没有完成)或者太慢(需要改进程序结构或是内部算法)而不能完成真正的工作。举例来说,查询一个有8000条记录的数据库需要1个小时(天哪,我想我连10分钟都等不了),虽然说具备了查询的功能,但是实在很令人怀疑此项功能是否会有人使用,更糟糕的情况是:由于用户使用了该功能而造成的恶劣印象难以在短时间内消除――虽然对于开发人员来说那可能只是一个参数拼写错误了而已。
4)遗漏功能
功能没有实现,却出现在了用户手册中。或者是本来应该具备地特征性功能,在程序只能看到一个“影子”(有其名无其实)。多半情况下是由于需求变更时没有对手册进行检查和更新,也有可能是因为遗漏了需求说明中应包含的功能(如果是那样,需要好好检查以前的工作方式是否正确)。
5)错误功能
一个本来应该完成查询工作的功能却干了排序的活儿。这种疏忽一般不是因为没有实现功能,而是在分配功能的时候出现了问题,当然这种情况的发现和排除应该不是一件困难的事。
6)功能性必须由用户创建
最常见的情况之一就是要求用户自己配置软环境(如配置数据源,一般都可以在安装程序中自动完成;当然还包括程序用到的组件在系统中不存在,安装程序没有提供相应的支持,这对用户是不能接受的)。这类问题不完全一定都是错误,比如微软提供的Office宏的开发,是为了满足客户对于自身特色而设计的适合其专业工作的程序。
7)不能做用户期望的工作
例如,极少有人会期望一个本来编写用来对姓名进行排序的程序却按照ASCII码的顺序排序。他们也不会指望用它来计算首位空格或区分大小写。当然用户名的排序还是要做的,问题是开发者需要重新构想一个新的排序规则来满足用户需求。
2、人机交互
人机交互,程序与操作者之间的通信与交流。这不是早些年的科幻电影,我们也许每天都在做,在取款机前,在自动售卖机前――
1)遗漏信息
你应该知道,所有的事都能从计算机屏幕上得到有效的消息。不要遗漏任何对于用户而言至关重要的信息,即使这些信息对你而言毫无用处。
l 没有任何屏幕指令
如何找到程序的名称?[ 4] 如何退出程序?你应该怎么样获取帮助?如果程序使用了某种命令语言,如何才能得到命令列表?程序可能仅仅只在它启动时显示这些内容。当然你也可以从帮助手册中获取这些信息,但并不是必要的。没有任何屏幕指令的程序可能会让人受不了,查询手册的话需要花费的时间可能会更长——这可能就会让用户觉得软件学习起来太复杂了。
l 假定打印出的文件随时可得
丢了用户手册怎么办?有经验的用户不会非要依赖打印好的文档,提供一份电子版的吧。
l 无正式文字证明(说明)的功能特征
如果大多数的功能特征或命令在屏幕上提供文字说明,那么所有的都应如此。仅略过几个功能特征将会导致UI形式上的混乱。同样,如果程序为很多命令描述其“特殊情况”下的行为,那么所有的命令都需要提供这类说明。这种情况在国人的软件开发过程中时有发生,并不是不能,而是不想……[ 5]
l 看起来不可能退出的状态
如何取消一条命令或在一个深层菜单树中进行备份?程序应该允许你可以避免那些你不希望遇到的情况。比如,在软件安装时,要求插入磁盘,如果不插入正确磁盘就不能退出安装程序。没有告诉你如何避免就和发生灾难时,没有提供一条逃生路径一样糟糕。
l 没有光标[ 6]
大多数用户都依赖于光标。一个光标可以让用户觉得计算机仍然在正常运转(尽管有时候死机也是如此),每个交互程序都应该显示光标,当然,在关闭光标时别忘了提醒用户注意。
l 没有对输入做出响应
每个程序都应该对输入做出回应。如果没有,呵呵,保管80%以上的用户产生疑问:怎么没有响应?还要等多久?是不是我的电脑过时了?[ 7]
如果有以下几种情况,一般视为正常:
2.Ø 选择一个菜单项时,如果你的按键操作没有回应,只要下一个屏幕立刻出现并且此屏幕上的标题与菜单项一样,就可以视为正常。
3.Ø 如果程序忽视错误的命令或按键操作,当然可以不对其进行回应。
4.Ø 如果你告诉程序不要对输入回应,它必须沉默,如果它进行了回应,应该立即告诉开发人员对其进行修改(可能是在忘记了继续处理另一种情况)。
Ø 如果输入的是安全性代码(如密码等),那么程序决不应在屏幕上做出不恰当的回应(如显示你输入的密码明文)。
l 在长期延迟时没有表示其活动
给一段较长时间的程序延迟一个合适的响应,将是非常必要的举动。相信这样不需要再给用户做出过多的解释了。
l 当某个改变即将生效时没有给出建议
一个程序可能会比你预计的更早或更晚执行一个命令,例如:删除某些重要数据时,(而这个过程将持续一段时间),必要的提示是必须的。
l 没有对已经打开的文件进行检查
这个错误是非常常见的,尤其对于那些输入关键数据的程序而言。用户可能不会注意,但是在以后的工作中将发现略微的一丝更改就会引出大量的问题。你不能保证程序会对同一个文件在某个时刻做出不同修改所带来的后果。所以,决不允许同一文件同时被打开两次甚至更多,以确保程序输出的唯一性。
l 错误的、误导的或令人迷惑的信息
每个错误都会让你对程序显示的所有其他东西产生怀疑。使用户做出虚假归纳的细小错误,如遗漏条件或是不适宜的类比会比清楚的事实错误更让测试人员感到恼火――因为更难对它们进行改正。
1.Ø 简单的事实错误
在一个程序改变之后,更新屏幕显示是低优先级任务,结果则是:屏幕上大量的信息过时。记住:无论何时程序发生改变,都要仔细检查可能指示程序特征的每个消息,最简单的办法就是直接对更改后的屏幕进行刷新。
2.Ø 拼写错误(错别字)
我相信,这绝对不是设计上的问题,我也相信开发人员可能会不以为然。Oh,但是客户会在乎,会抱怨这些的――还是改正它们吧。
3.Ø 不准确的简化
在保持一个特征的描述尽可能简单的愿望中,一条消息的创作者可能只覆盖特征行为的最简单方面,而忽略了重要条件。举例来说,这种情况可能会引发歧义,比如说关闭(到底是关闭文件还是关闭程序?)。作为一个测试人员,需要你足够仔细的研究可能会出现问题的任何一个微不足道的细节。宁可错杀,不能放过!(虽然要极力避免错杀的情况。)
4.Ø 无效的比喻(图标之类可以指示功能(特征)的事物)
例如:回收站的图标可能不是一个好的比喻;对于文件一旦移除就永久消失的情况来说,碎纸机的图标可能来得更好一些。
5.Ø 令人迷惑不解的特征(功能)名称
如果一个命令名称在计算机领域中或语言中有一个标准含义,就必须与其含义一致。别指望着胳膊能拧得过大腿,确定现行的标准是可靠的。
6.Ø 同一特征(功能)具有多个名称
相同的功能特征只要一个名称就够了――只要能表述清楚;用户可不一定有时间玩同义词的游戏。另外,这种情况对软件在用户面前显示的复杂度也有影响。
7.Ø 信息超载
不要让你的文档和帮助屏幕看起来太过专业――太多技术细节了。用户会不耐烦的,况且效果也不好。如果实在需要,可以把他们另外列出。尽量使用直白,用户能理解的话表述这些信息。另外,信息超载的另一个意义意味着烦琐冗长的语句,那是要极力避免的。
8.Ø 数据何时得到保存
假设你输入了一些程序需要保存的信息。当你进行切换或程序退出时,当你需要每隔一段时间进行保存时,它是否会把数据按照你想的方式进行保存呢?何时完成呢?如果你对答案感到困惑,那就意味着可能有问题出现了。曾经在同事的项目中发现过很多次这样的情况:每次修改后直接关闭程序,却不提示用户保存――我只知道,修改信息在关闭时也消失了。在对某个模块进行修改时,你应密切注意这个问题。
9.Ø 很差的外部模块性
外部模块性指的是程序从外部看起来产品的模块化程度(如同程序是可分割的实体)。你如何容易地理解模块组件?很差的外部模块会耗费大量的时间来学习产品,还会吓跑新用户――它看上去太复杂了!尽可能让信息独立展示出来,对任何特定任务而言,你需要知道的越少越好。
2)帮助文本和错误信息
帮助文本和错误信息通常被认为是产品的次要部分。它们可能是由低级程序员编写的(比如我)或是作者编写,对其进行更新的工作可能被赋予低优先级。然而,作为产品而言,这又是必不可少的部分,一份看上去赏心悦目的帮助文档可以“主观[ 8] ”的降低软件的学习难度和用户的使用兴趣。
当你感到困惑或是有麻烦时,寻求帮助或倾向于使用错误处理程序将是一个明智的选择。你可能会觉得不爽,更多的时候是不耐烦。而如果其中有信息误导了你,那么无异于火上浇油。以下列出的是我以往在审核帮助文档及错误信息时碰到的一些常见问题。
1.l 不合适的阅读层次
在计算机终端上,人们不能很好的进行阅读。帮助文本和错误信息应该尽量措辞简单明了,多用主动语态,尽量少使用技术术语,即便用户中有计算机经验也是如此。
2.l 冗长[ 9]
避免你的帮助文档和错误信息变成裹脚布。大多数用户在需要更多信息时,会选择通过菜单获得进一步的信息。最好让他们自己选择所需的信息。
3.l 不合适的情绪语气
尽量不要使用感叹号,如“终止”、“崩溃”之类的带有激烈意味的词语都应如此。
4.l 事实错误
记得测试时需要测试那些更新过的功能,在旧的帮助上的方法可能在新软件中是行不通的。这个时候开发人员的代码更新日志就显得很必要了,你不必对每项功能进行检查,完全可以把这类回归测试交给自动化测试工具完成,而你只需要关注其新内容即可。
5.l 上下文错误
不要把上下文之间的关系搞错了,这会带来阅读时的不便。比较清晰的方法是首先列出上下文关联列表,并按照操作顺序的先后进行组织。[ 10]
6.l 没有识别出错误来源
通常,一个好的错误信息不仅可以指出是什么情况,而且还要指出为什么有些东西出了错,以及如何处理此类错误的方法。在过往的项目中,常常有很多这样的问题:如“打印错误”、“保存错误”等等含糊的说明。如果用户在获得了错误信息后,还是一脸茫然,那就应该认真考虑一下错误说明的编写方式是否可以再改进一些了。
7.l 不能立刻重现的错误很可能是个大问题
8.l 没有说明原因就禁止一个资源[ 11]
如果程序尝试使用打印机、内存控件等资源,却做不到,错误信息应当包含的不仅仅是宣布失败,还需要说明原因。
9.l 报告说没有错误
首先,还是先承认这种情况是不太可能会出现的;错误信息只能由错误状态出发,如果大部分是通常情况的调试消息,或者是少部分并不一定由某个缺陷引起的事件报告,那么,你可能就会忽略所有的错误信息。
3)显示上的(问题)缺陷
这是一个比较客观的问题,至少表面上看上去是这样的。任何可见的错误都会产生让人不快的感觉(尽管这些问题不一定很严重),用户就不一定会相信或者购买该产品。可能是因为此类错误大多都是属于低级错误,通常并不受到开发人员和项目经理的重视,但是我们必须重视它――它就是问题(Bug),它就是我们要找的东西之一。
另外提一点:总是拘泥于这类Bug可能会使测试失去意义――放过重要的功能需求,“吹毛求疵”[ 12] 。它可能是造成开发人员和项目经理不重视测试的一个借口。尽管如此,我们还是要提出这类问题,但并不是说可以遗漏重要的功能需求。
l 数据写到了错误的屏幕位置
光标提示在正确的位置,但是数据却显示在屏幕错误的位置(张冠李戴)。这类错误对开发人员而言,不应该是个很棘手的问题,但对用户来说,那是致命的。
l 未能清除部分屏幕
一条信息在屏幕上显示了几秒钟,接着却只有一部分被擦除了;或者你对前一问题的回应仍然留在屏幕上,我们固然可以以计算机整体性能为借口,但也不能排除技术因素。为了输入一些新东西,不得不在提示或不相关的回应上输入,这是令人头疼而且迷惑不解的。在以前测试的项目中,就曾经出现过由于屏幕未正确刷新导致的清屏不完整及无故弹出不相关提示的问题――这种问题比较普遍,需要多加注意。
l 未能突出显示部分屏幕
如果程序常常需要突出显示某个特定类别的项目,例如提示或者在激活窗口中的所有文本,那么它就必须一直这么做。[ 13]
l 未能清除突出显示
屏幕位置的属性与显示的文本分开储存时这是很普遍的。程序员删除了突出显示的文本,但是忘记了从屏幕的那一区域清除突出显示。这类问题一般都和数据刷新有关系,无论是界面上的处理还是系统底层的处理。
l 显示的字符串错误或不完整
显示的消息可能是毫无价值的文本,一个冗长的信息的一个片断或是一条应该显示在其他时间出现的完整信息。这其中任何一条都可能反映出程序逻辑上,用来发现消息文本的指针的值或者已储存的文本副本中的错误。
l 显示信息过长或者不够长
消息在屏幕上显示的时间应该足够长,至少应该保持到能让用户读到结束为止。如果对同一条消息有时显示时间长,有时显示时间短则需要注意,这可能预示着外部资源之间的竞争条件(比如对内存资源的争夺),往往这些条件是在我们考虑之外的,需要认真对待。
4)界面布局的显示
屏幕看上去应该是很有条理的,别让它看起来像是一个乱糟糟的房间。不同类别的对象应该在可预知的区域分开显示。你可以参考一些关于UI布局的文章,但归结起来说:显示布局应该很容易让你在屏幕上找到你要的东西。
l 从美学角度看屏幕布局很拙劣
屏幕可能是不平衡的,大多数情况下是这样子,行或者列不对齐,或者只是看起来很“糟糕”。好好利用你的鉴赏力,如果没有信心,可以问问别人的意见,参考一些界面设计很合理的软件。如果对你而言它的布局的确看起来很糟,相信你的直觉,肯定有什么东西错了,尽管现在你还没有发现。
l 菜单布局错误
这是最常见的问题之一了:我们有时候会发现在编辑菜单下突然冒出了一个文件关闭的选项,而一般它是放在文件一栏下的。在很多的参考文献中,已经对这方面的内容做了比较详实的说明,我想强调的是以下一些问题:
1. 相似的或从概念上相关的菜单选择应该分组,或者应该在屏幕上说明。
2. 选择一个菜单项通常应该独立。为了获得一个独立的结果,用户不应该必须在不同的菜单上做出两个或更多的选择(这可绝对“难”用)。
3. 通过键入其首字母来选择菜单项通常要比使用数字来得好。当然,你要留神不要给菜单项过于奇怪的名称;另外,还要注意不要在同一栏下面不要出项重复的字母。
l 对话框布局错误
就强调几点吧。
1. 对话框应该一致。如:他们应该一致使用大小写,字体和文本对齐规则。对话框标题应当占据某个一致的位置,并与用来调用该对话框的命令名相符合。相同的快捷方式在不同对话框之间应该起相同作用――如<ESC>不应取消某些对话框,而在其他类似情况下完成其他的任务。
2. 对话框中的控件布局必须合理安排。应使用必要的间隔把组隔开。
3. 选择和录入区域应该垂直和水平排列,这样用户就可以以直线模式操作光标的运动(为了方便)。
4. 留意对话框之间的相互依赖性[ 14] 。如果某个对话框中的选择将最终决定另一个对话框的选项将是令人困惑的。如果程序不得不这样做,务必要求开发人员给出具体提示。
l 模糊不清的指示
你应该总是知道去哪里查找以找出下一步。如果屏幕排得很满,总是应该为命令和消息留出一块空间。使用气泡显示信息也是一个不错得选择。
l 闪烁的误用[ 15]
闪烁的图片或文本很引人注意,不过记得不要太多闪烁。太多的闪烁会让人觉得不舒服。你应该每次最多只让一个目标进行闪烁而且频率不能太高。
l 颜色的误用
不要太多颜色,它会让我们的眼睛很疲劳。颜色不应该使我们分散注意力,也不能使屏幕看上去杂乱无章,尽量使用统一风格的颜色,如果程序的颜色组合看上去很难看,抗议吧,没有人会愿意买毫无美感的产品的。
l 过于依赖颜色[ 16]
如果程序在项之间使用颜色为唯一分隔符,那么它将限制使用者的范围,对于一些特殊的产品,需要考虑到例如色盲之类对颜色不敏感的人群或是使用单色显示器的用户。
l 与整体风格不符合[ 17]
如果与计算机相关的风格提供了某种一致性和便利,尽量好好利用。也许对程序员来说可以使用更好的技术来代替,对于用户来说也未必不是不可接受的。例如:在习惯了鼠标和图标之后,恐怕很少有用户会习惯敲击键盘书写命令来完成计算机可以使用鼠标完成的工作。当大多数其他的程序以某种特定方式在屏幕的特定位置显示错误信息时,新程序也应该是这样的。
l 不能去掉屏幕上的信息
在屏幕上某个部分的可用命令选择菜单是很好的选择。一旦用户精通了程序,有些菜单就会成为屏幕空间资源的浪费。你应该可以提交一个命令能去掉和重新调用它。这点上,值得向微软的Office系列软件学习。
3、命令结构和录入
这里只处理实现中的缺陷。(即假定程序员对风格的选择是合理的)
1) 不一致性[ 18]
增加永真规则的数量可以缩短学习时间,并减少文档,而且使程序看上去更专业。不一致性如此的普遍,是因为它需要规划并进行斗争来选择能一直遵循的操作规则。每个微小的不一致性都是不重要的,但是一旦达到了一定量,一个本来构想很好的产品有可能会变得难以使用,甚至变成废品。从开发人员本身来讲,也体现出其开发本身的严密性。一个好的测试实践要标注出所有发现的不一致性,无论多么微不足道都要如此。
2) “最优化”[ 19]
程序员有时候会有意引入不一致性来对程序进行优化。的确很吸引人,但是也要注意优化所带来的风险和一些优化的必要性:保存一两次按键操作是否与学习时间的增加或信任度的减少价值相当?未必。
l 不一致的缩写
如果没有很明确的缩写规则,缩写就不能容易地被记住。把Delete缩写为Del,把Grep缩写为Grep,是没有任何意义的。
l 不一致的终止规则
程序应该为多重键录入要求终结符。
l 不一致的命令选项
如果一个选项对两个或者更多的命令有意义,那么它就应该对这些命令都可用(都不可用),它应该具有同样的名称,并且应该在两种情况下以同样的顺序被调用。
l 名称相似的命令
如果两个命令名称相似,就很容易搞混。尽量不要使用相似的名称命名命令。这个问题在中文界面的软件中表现得尤为突出。
l 不一致的大写
如果命令录入是区分大小写的,所有命令的第一个字符都应该使用大写(小写)。命令中嵌入单词的第一个字符应该一直大写(小写)。另外,如非必要,不要在一个命令中使用多国语言。[ 20]
l 不一致的菜单位置
如果同一命令在不同子菜单中出现,那么要在不同菜单的同一位置保留同一命令是很困难的。这句话不是很好理解,不过把话说白了就好理解很多:要保持命令在同一子菜单中的位置,而不是让它东搬西迁在其他的子菜单中停留。
l 不一致的功能键用途与其说明
功能键的意义在程序中应始终保持一致(颠倒或是功能冲突是不能接受的)。
l 不一致的错误处理规则
当程序检测到一个错误之后,它可能会公布该错误,或者尝试更正错误。任何一个程序的行为都应该是可预测的。如果当提交错误数据时没有任何的提示或尝试更正错误的说明,那么用户就无法确认数据是否是干净的。
l 不一致的编辑规则
当你输入或稍后检查任何数据时,同样的键和命令应该可以用来对其进行修改。
l 不一致的数据保存规则
应该在每处都以同样的方式,在同样的时间和范围内保存数据。它不应该在每个区域输入数据时保存数据,而其他时间则在一个记录、一组记录的末尾保存数据,或恰好在退出前保存数据。
3) 浪费时间
看起来为了浪费时间而进行的设计会激怒每个人,应该把时间花在更有意义的事情上去。
l 曲折路径
如果为了到达想要的命令,你必须一个接一个做出选择。结果到最后发现,该命令不存在、不能实现或者要求你先完成某件事甚至几件事后才能使用――明显的欺诈行为!相信客户的不满和你(测试人员)的不满几乎没有任何区别。举个例子说:当用户辛辛苦苦填满了整整一页的数据,最后提交时发现:该页数据中的某项已经被使用了时,用户的焦躁可想而知。
l 不能采用的选择
事实上没有任何接口在一个不能建立的菜单中包含选择项。如果没有任何数据存在,你如何评审、保存和擦除数据?没有打印机,如何打印文档?有的命令不适合出现在某些条件下(虽然对使用没有什么影响),但是开发人员可能为了图方便而保留了此类命令(很遗憾地说:这太不专业了);有时候,程序会提示寻求帮助,而当你真的去使用的时候,程序却告诉你“您没有使用帮助的权限”――面对可望而不可及的东西,很多人宁愿选择不去看见。这种情况很常见,至于常常被开发人员和测试人员共同忽略――但这是不应该存在的错误。
l 你真的,真的确定么?
严重毁坏数据的命令需要这样重复的确认。是的,这是必须的,如:对一个写满数据的硬盘进行格式化的确需要多次确认。但是没有必要对每个细小的删除操作进行繁复的多次确认操作,用户会变得不耐烦,其结果有可能是:当用户真的在进行严重毁坏性命令时,无视屏幕提示,造成不可预计的后果。
l 模糊不清或者带有个人风格的命令
命令名应该明确指示该命令的作用。不要让用户一边使用软件,一边查询用户手册中关于该命令的解释。调查表明:大多数用户在使用软件产品的时候只对软件手册做大概的了解,甚至根本不阅读。别指望用户会很完整地阅读手册,那是我们的工作。没有任何理由在发布的程序中生成如:finger等带有明显个人风格的命令,当然,如果在程序中出现了脏话,我希望(仅仅是希望)客户没有气到火冒三丈。
4) 菜单
菜单应该尽量简洁,当存在了太多风格不一,冗长和拙劣的图标或命令名,以及当选择隐藏在不明显的主题之下的子项时,理解它们将会变得非常复杂。一个菜单覆盖的命令越多,无论如何规划,都会变得复杂,如果没有良好的规划,这对用户的使用将是一大障碍。
l 过于复杂的菜单层次
如果必须在最后到达你想要的命令之前很吃力地通过一个又一个菜单,那么我想我会选择使用另外一个功能相近的程序。创建深层菜单树的程序员所引用的设计规则表明,没有哪个菜单应该具有七个以上的选项,这一点对新手来说可能时最好的。有经验的用户更倾向于每个菜单级别有更多选择,犯更少错误并更快速有效的做出反应,只要选项能合理组织,整齐排版,而且没有可笑的拥挤或拼写错误就行了。
l 不适当的菜单导航系统
即使在一个最适当的深层菜单系统中,你也必须能够返回到前一菜单,或者移到菜单结构的顶层,并能在任何时刻离开程序。
l 太多路径到达相同位置
如果许多命令在菜单中重复出现,那么程序就需要重新组织。让一个命令在不同位置重复可能会很便捷,但是存在一定的限制。如果感觉上你可以从程序的任何位置到达另一个任意位置――那就得重新考虑下程序的内部结构和可靠性。
l 相关的命令被归属到不相关的菜单
把一个复杂菜单中对命令或主题进行分组并不是一件容易的事情。人们都很容易忽视两项之间明显关系,并把它们分配到分开的菜单中去,当需要对此进行调整时,我们需要做的是:解释两项之间的关系,并建议两者都应属于哪个菜单。
l 不相关命令被放置到同一菜单下
有些命令被扔到了完全无关的菜单下,这样并不好,宁可重新选择一个更高级别的标题并重新组织这些命令也不要那么做――如果那样做导致的混乱比较严重的话。
5) 键盘不能正确使用
不能正确使用键盘无论在何时都是一个问题。
l 无法使用编辑键或功能键
如果一个程序从某些其他没有这些键的机器上移植过来,那没关系。相反可能就不行。要确保程序可以使用已有的编辑键和功能键。
l 光标和编辑键的非标准使用
这些键应该按照他们通常在该机器上工作的方式进行工作,而不是按照他们通常在其他某个机器上工作的方式来工作。
l 功能键的非标准使用
如果大多数程序用F1作为帮助键,那么如果在程序中将它定义为其他的功能,那将是不合适的。
l 不能过滤无效键
程序应该能挡住并抛弃无效字符[ 21] ,比如进行数字相加时输入的字母。它不应该做出回应。与错误消息相比,这样做更快更有效。
l 未能指示键盘状态的改变。
键盘上的灯或屏幕信息应该告诉你何时你的Num Lock键和其他类似的状态转换特征是开着的。
l 未能扫描功能键或快捷键
l 你应该能够告诉计算机从它正在进行的工作中退出;程序应该总是能辨别出任何其他系统指定的键――即那些本机上的程序通常可以很快识别出来的键。
4、遗漏的命令
1)状态转换
大多数程序从一个状态转到另一个状态,在你选择某个菜单项或者提交一个命令之前,程序处于某种状态。为了回应你的选择,程序回到另一个状态。程序员通常会对他们的代码进行足够的测试,以确保你能达到任何你应该可以达到的状态。
l 什么都不作就退出(状态返回)
你应该能够告诉应用程序,你做出的最后一个选择有误,并返回到其前一个状态。
l 不能在程序中间退出[ 22]
当使用一个程序但还没有对存储的数据造成不利影响时,你应该能够从中退出;如果你正在编辑的文件出现了预想不到的错误,在中止后应能回到先前保存过的状态。
l 不能在命令中间停止
告诉程序停止一个命令很容易,而返回到起始点或选择一个其他的命令也应该不太难。如果其中任何一个方面出现了问题,就需要重新考量先前的设计是否真的合理了。
l 不能暂停
如果程序限定了你输入的时间,时间一到,状态就改变,那么当你离开时你就需要它暂停一会儿。这中类型的情况在游戏软件中见得较多[ 23] ,比如说暂停游戏。
2)危机预防[ 24]
系统故障和用户错误发生了,程序应具备将其后果降到最低的能力。
l 没有备份工具
对开发人员而言,为了一个文件做一个额外备份应该不是一件困难的事。如果你正在修改一个文件,计算机应当保留原始版本的一个副本,因此如果你的更改有错误,还能返回到一个已知的好的版本。
l 不能撤销
撤销一个你已经发出的编辑命令,至少是一步。恢复被删除的文件是一种受限制的撤销,它能让你恢复错误删除的数据。撤销是可取的,恢复被删除文件也应是必须的。
l 没有“你确定吗?“的提示
提交一个大量数据清除的工作,或者提出一个清除少量数据但是会影响其他作业的命令或者很容易错误提交的命令,都需要程序在用户操作时进行确认,不声不响地进行将会带来安全方面的隐患(尤其是在后台进行暗箱操作时,这种危险性更高)。
l 没有增量保存[ 25]
当输入大量文本或数据时,你应该能告诉程序相隔一定时间对你的工作进行保存,至少应该提供用户此类选项。对于突发的掉电和硬件损坏情况这样做将是非常有好处的。
3)由用户进行的错误处理
人们可以捕获自己的错误,而经验告诉我们,他们还容易犯其他的错误。他们应能自理修复错误,并建立自己的错误检查机制。
l 没有用户能指定的过滤器
当设计数据录入表格和电子表格模板时,你应该能够让每个区域指定什么样的数据类型有效、程序应忽略或拒绝什么。例如,你可以让程序拒绝数字、字母、不在某个特定范围内的数值,一个有效日期或者与磁盘上匹配的日期等等。
l 难用的错误更正
修改一个错误应该很容易。不应该因为犯了错误的数据录入而让整个系统重新启动。在输入一串数据时,你应该能在不重新输入剩余部分的情况下,更正错误的数据。
l 不能包括注释[ 26]
当设计数据录入表格,电子模板,专家系统时,你应该能够为未来参考和调试输入注释信息,这是很必要的。
l 不能显示变量之间的关系
录入表格、电子模板中有些变量是相互关联的,应该能很容易的检查任意变量对其他变量的依赖性。这在设计时应该被周密考虑到。在大多数的财务管理系统中,这类应用是较普遍的。
4)其他问题
l 隐私和安全性[ 27]
对于某些特定的程序,需要考虑数据的充分安全性,保护公司,集体或个人的机密和隐私。在多用户系统上,你应该能很好的隐藏你的文件(一般都是通过加密手段实现的),并且能很好的锁定你的文件不被篡改或阅读。
l 安全性的困扰
一个程序的安全性控制应尽可能谨慎考虑与实施。
l 隐藏菜单[ 28]
很多程序在顶端、底部或屏幕边缘显示一个菜单(包括我以前测试的大多数应用程序)。它们使用屏幕的剩余部分作为数据录入和处理的区域。菜单只是记忆的辅助物。一旦用户了解了他所需要的所有命令后,就应该给予用户充分的自由,让其选择是否需要保留菜单的权力。
l 不支持标准O/S特征
例如:如果系统使用了子目录,那么程序就应该能引用其他子目录。如果操作系统提供了通配符(比如*),那么程序也应该能使用。
l 不允许长名称
应当允许使用长名称(只要不是太离谱),毕竟,内存不足和编译器反应迟钝的时代已经成为过去了。别让自己的程序也成了文物。
5、程序僵化
程序有灵活与固定之分。灵活的程序更显个性化一些,而固定僵化的程序一般都是由于业务流程的关系而不得不如此。如借还书的过程,必先借了才能还。别给用户太多的自由,否则程序看起来会让人觉得松散,也不要把程序定得死死的,那看上去给人太多压力了。
1)用户可调整性
l 不能关掉噪音
犯错误时,许多程序都会给出“咄”的警告声。而如果每次接触键盘都发声,这简直是不能容忍的――特别是在公共场所。必须关闭没有必要的噪音,至少程序要提供这类控制选项。
l 不能关闭大小写区分
一个能区分大小写的系统应该允许你告诉它忽略大小写的问题。
l 不能配合手边的硬件
有些程序被锁定针对了特定的输入输出程序。升级或更换了设备的用户可能就无法使用这些功能了。这是令人遗憾的。同时,也会让用户觉得是否是一种商业捆绑的模式而拒绝使用此产品。尽量让开发人员编写通用的硬件接口代码以适应大部分通用的硬件设备。
l 不能改变设备初始化
一个应用程序应该能够发送用户定义的初始状态,或者至少应该让它保持现状。每次启动都需要重新配置将会是很烦人的工作。假定你想要向一个打印机发送控制代码,以转换到压缩字符,如果打印数据的程序不让你初始化打印机,你就不得不从设备来改变打印机的模式和状态,然后重新运行程序。如果程序阻挠了你的打印机设置,那就是一个设计错误[ 29] 。
l 不能关闭自动保存
自动保存是件好事,不过无事生非也是一件糟糕的事情。过于频繁的自动保存可能会让用户觉得程序不可靠。所以还是老老实实加上关闭自动保存的选项吧。
l 不能改变滚动速度
严格来说,这不是一个很严重的问题。目前很多的设备驱动中已经提供了此类选项。
l 不能重复上次的操作
这样的例子比如Word软件中的Redo。
l 无法找到你上次完成的内容[ 30]
此类问题对数据编辑特别是文本编辑类的程序较为常见。应当提供保存的文件列表,除非用户禁止了它。
l 无法执行一个定制的命令
在程序选项中,一旦对其更改,应该立即生效,不需要再次重新启动程序进行加载――特殊情况除外(如果你无法避免)。
l 无法保存定制的命令[ 31]
你不应该只告诉程序此次运行关闭了警告声,而是应该让程序永远可以关闭这些――只要设置没有发生变化。
l 特征更改的副作用
这种情况较常见,修改了某一个特征,相关的特征也会改变。如果确实存在副作用,应当在手册和屏幕中提供详实的文档证明和提示。
l 无限可调整性
开发人员可以改变某些程序的方方面面。但是在开篇时说过,提供了太多灵活性的程序并非一直都是一件好事。好好斟酌再做决定要比草率地修改来得更好。
2)控制方式
有些程序很“霸道”。它们的错误信息和帮助消息自认为高人一等,它们的风格不可原谅的不便――你甚至无法放弃命令或者在输入数据后进行更改。程序应该使你觉得能更容易,更惬意地完成一个任务。至少,它不应该浪费你的时间。
l 一个概念风格的不必要和不合理要求
有些程序要求你以某种顺序输入数据,或要求你在进行下一步之前先完成某项任务,再要么就要求你再考虑它们的潜在后果之前做出决定。例如:
当设计一种数据录入格式时,为何你必须再屏幕显示之前确定一个数据录入域的名称,类型,宽度或者计算顺序?当你察觉不同的域放在一起看起来有神么不妥的时候,你是否会更改某些域,把它们的位置换来换去,甚至去掉少数域?你可能不得不在使用该格式之前输入域的规格说明,但是在该约束条件下,你应该决定何时填充细节。
当向一个项目管理系统描述任务时,为何你必须首先列出所有的任务,然后列出说有可用的人员,接着在为下一项工作输入任何数据之前就把分配给某个人的工作完全对应?既然你可能试着决定什么工作分配给什么人,那么你不想看到结果后再更改这些数据么?
限制的数量如此多,是因为有些开发人员认为,人们应该以某种方式组织它们的工作。但是他们所想的最佳途径未必都是最佳的。我们应该更清醒地意识到,除了业务流程上的禁锢,不需要再对用户在风格上多加任何的限制――当然,如果用户需要的话。
l 对新手友好,但是对老手并不一定友好[ 32]
为初学者设计的进行过优化的过程可能为他们掌握系统会有帮助,但是同时会令一些有经验的老手带来烦恼。他们更希望能自由地使用软件;其中一个解决办法就是提供两条以上地路径实现对不同层次用户的需求。
l 人工智能与自动化
有些更智能和更便利的程序会猜测你的下一步动作,并枉加执行这些猜测;自动纠错的程序的确很好,除非它“纠正”了正确的数据。而有时候,用户并不一定希望这样做。提供可用的选项可以缓冲一下这方面的矛盾。
l 过剩或多余的必需信息
过剩和多余的必需信息就像我们身上的赘肉一样讨厌。有些程序会请求他们从来不会使用或者只会用来在屏幕上显示一次的信息,或者会要求你重新输入你已经输入过的数据――并非是为了验证,仅仅只是重新获取一次数据,这对时间的浪费是非常惊人的。
l 不必要的步骤重复
如果你在输入一个较长的命令步骤或数据时犯了一个错误,有些程序就不管青红皂白让你重新输入;有的程序则强迫你重新输入或确定可能有错误的任何命令。为了某些任务,你甚至可能需要确认每个步骤。相信我:不必要的重复或确认都是浪费时间。
l 不必要的限制
为什么要把一个数据库限定为如此多的字段或记录,或限制一个电子表格仅使用数字,把一个项目经理限制在如此多的任务中,一个字处理程序限制在如此多的字符呢?对性能或可靠性而言并非必要的限制不应该成为约束条件。
6、性能
许多有经验的用户认为性能是最重要的可用性因素:使用一个快速程序,他们感到更能集中精神工作,而且更多的东西处在掌握之中。在极少出现异常的情况下,程序越快越好。
性能有很多定义,大致来说主要有如下的感性认识:
1. 程序速度:执行标准任务的速度有多快?
2. 用户吞吐量:你能使用程序执行标准任务的速度有多快?
3. 感觉到的性能:在用户看来,该程序速度有多块?它是否令你满意?
无论如何定义,程序速度总是一个关键因素。但是一个界面设计拙劣的快速程序无论怎么看都要比它实际的运行和处理速度要慢得多。
1)降低程序速度
很多设计和代码错误会降低一个程序的执行错误。程序可能会执行很多不必要的工作,如对一个在读前会被重写的内存区域进行初始化;也可能会对工作进行不必要的重复,如在一个在循环中执行的任务可以在循环外完成;设计的决策也会影响到程序的速度,而且通常要比明显错误导致的慢速情况更加严重。[ 33]
2)缓慢回应
程序应立即对输入做出响应,如果在你输入一个字母的时刻和你看到这个字母的时刻有延迟,显然,程序就太慢了。快速反馈对任何输入事件都必须是有效而且是必要的,它包括:鼠标,键盘,轨迹球,键盘等等。
3)如何减少用户吞吐量
一个闪电般快的程序执行任务时可能比蜗牛还要慢。这包括:
1. 任何可能使用户错误更可能发生的事情。(培训不周,用户习惯,程序风格等等)
2. 缓慢的错误恢复。如:在输入一长串字符后发现错误却必须要重新输入。
3. 任何使你感到迷惑,却得不到帮助文档或手册提供资料的事情。
4. 输入很多,却做得很少的程序――这不是一个好程序。如:把一个简单任务划分成很小的子任务,要求对所有事情进行确认等等。
在测试时,使用比较性测试是个有效的方法:即把开发中的产品与竞争对手的产品进行比较,如果人们使用你的产品花费的时间要更长,那么发现这个问题就是有意义的。
4)反应拙劣
我曾经测试过一个产品,在输入第一条数据后,居然花了将近一分钟才从数据库中将数据取回。这不能不说是一个反应很慢的程序。一份表单做下来将近300条数据,按此速度计算,我将花上至少半天才能完成输入。这种情况是不能允许的。迅速地对输入做出回应是一个程序最最基本的功能。一个反应迅速的程序不会强迫你在提交下一个命令之前让你持续等待,而是让你继续做其他事。
5)没有提前输入
一个允许提前输入的程序会让你在它从事其他工作的时候仍然可以键入,它会记住输入内容,加以显示并在稍后进行处理。你不应该等着输入下一个命令。
6)没有给出某个操作会花很长时间的警告
如果程度需要超过几秒钟的时间来进行某事,程序应该告知用户。对于较长时间的任务,它应该给你一个大概的时间印象而不是让你干等着它结束。给出大致需要完成的时间或是进度条是处理此类问题一般的方法。
7)程序太多提示和询问[ 34]
提示,警告以及询问可能很有用,不过如果它们出现得太频繁,就会让人很窝火。
8)尽量使用简单命令和提示
在慢速终端上,帮助文本,长菜单以及漂亮的图片常常会令人不耐烦。你应该使用简要的语言取而代之。不要使用诸如“你真的想以500k/s的速度传送此邮件到某邮箱”么之类罗嗦的语句。
7、输出
程序的输出应如输入一样完整。它要求更精确,尽量快速和能实现多路径及对输出内容更有效的管理,这四类标准几乎决定了输出功能的主要表现特性。
1)不能输出某种数据
你应该能打印出你输入的任何信息,打印不出输入的内容对任何程序而言都是致命伤。
2)不能重定向输出
你应该可以重定向输出。特别是,你应该能向磁盘发送一个很长的“打印输出”标记,并稍后打印该磁盘文件。程序不应该阻止你把数据输出发送到预料之外的设备,如绘图仪,磁带,打印机等。
3)与一个后续过程不兼容的格式
如果一个程序声明能够以第二个程序可以理解的格式保存数据,那么就应该测试它是否可以真正做到。这意味着购买或借用第二个程序的副本。使用第一个程序保存数据,用第二个程序读数据,同时看看第二个程序得到了什么,这是对此进行测试的最简单方法。
4)必须输出的很少或很多
你应该可以修改报告,从而呈现你需要的信息。不得不在仅包含少量有用信息行的打印输出的大量页中找出所需信息,几乎和没有得到信息一样糟糕。
5)不能控制输出布局
你应该可以改变字体,对输出信息增加特殊标记来强调信息。你应该可以修改信息之间的间距,最低限度来说,程序应该可以以一种由合适文字处理进行修饰的格式把报告输出到磁盘文件。
6)荒谬的精度输出级别[ 35]
要是说4.2加上3.1等于7.3000000或者说3.1111+2.11等于5.22110102都是很愚蠢的。在最终输出的结果中,程序应该按照规定的格式和精度输出最后的数据。
7)不能控制表或图的标记
你应当能够改变字型,措辞及任何说明,包括标题,表格,图形或是图表中文本的位置。
8)不能控制图形的缩放比例
绘图程序应该提供默认的垂直和水平比例,不要告诉我你最后输出打印报表中的图形超出了整个页面或是只占据了整个页面的一角。
二、错误处理
在处理错误时发生的错误通常是最常见的缺陷。错误处理产生的错误包括:未预料到错误发生的可能性并防止其发生,没有注意错误状态,以及较严重的:程序可能与错误数据一起工作并最终产生错误结果的情况[ 36] 。
1、错误预防
程序应具备这种能力:它能保护自己不受到系统其他部分的影响(包括有害输入和有害处理)。如果程序可能与错误数据共同工作,确保其在发生严重可怕的影响之前(如程序崩溃,数据丢失与错误,系统崩溃等),检查并消除这些问题。
1)不充分的初始状态验证
如果内存的某个区域必须以其中所有位都是0开始,那么程序应该可以运行一个抽样检查,而不是假定已经存在0值。这会导致程序初始化时发生内存错,甚至不能启动。
2)不充分的用户输入检查
此类问题非常常见,开发人员可能会在编写程序时遗漏掉大量这方面的问题。告诉人们输入1位到3位数是不够的,有些人可能会输入5位甚至更多,也有人会输入特殊字符或是运算符,还有些人会按下功能键一次或多次,如果程序允许输入,那么程序就应能顺利应付,而不是一打非专业人士不能明白的提示甚至更糟的情况。
3)对受损数据不能充分预防
没有人能保证磁盘上的数据是好的。可能是有人已经编辑过或者根本是有硬件问题。即使开发人员认定在保存前的文件是有效的,那么他也应该检查(校验)下次打开的是否是同一个文件。
4)不充分的参数传递测试[ 37]
一个子程序不应该假定得到了正确的调用(事实上,采用相反的想法可能会让测试进行得更加顺利一些)。它应该确保传递给它的数据在其可控制的范围之内。
5)针对操作系统的预防不充分
操作系统存在缺陷――不光是过去,现在甚至将来也是,应用程序可能会触发其中存在的问题。如:如果开发人员知道,他把数据送到磁盘后很快又把数据送到打印机,会引起一些操作系统的崩溃,那么他就应该确保程序在任何情况下都不会这样做。
6)不适当的版本控制[ 38]
如果可执行代码不止存在于一个文件中,那么有人会尝试把某一文件的新版本和老版本一起使用,客户对其软件升级使得这类问题时常发生,但是又不能明白出了什么问题。新版本应确保所有的代码都是最新的,当然也要对老版本有完整的备份。
7)针对恶意使用的不充分预防
人们有时会有意无意的提供程序有害输入,或者尝试触发错误状况(特别是做测试的家伙们)。不要假定“任何有理智的人都不会这么做”,相信我:程序不完全是为了“有理智”的人开发的。
2、错误检测
程序通常有足够的可用信息来检测数据中或其操作中的错误。这部分内容将指导一部分常见的错误检测方式并对其进行归类。
1)忽视溢出
一个数值计算结果对于程序来说太大以至于无法处理时,就会产生溢出。溢出由较大数字相加和相乘或者被0除或是由于过小的分数除而引起。在有既定规则的情况下,溢出是很容易检测到的。
2)忽视不可能的值
在计算机当中,几乎没有什么不可能发生的事。程序应该检查其变量,以确保它们在合理的界限之内,它应可以捕获并拒绝如2月31日这样的日期值。如果当变量为0时,程序完成某动作,变量为1时完成另一工作,并假设其他所有值都是不可能,那么就必须保证变量只能为0或者是1,对其他所有值进行额外的处理。在一个项目中,经过数年维护编程后,旧的假定就不一定安全了。[ 39]
3)忽视看上去不真实的值
有些人可能会从自己帐户里提取100,000,000,000的钱,那么就算他有这么多钱,也应该在通过交易之前向几个不同的人进行确认。这类看似荒唐的用例往往包藏着错误的祸心,应该小心应付。
4)忽视错误标志
程序调用了一个子程序,结果操作不成功,它在一个被称为错误标志的特殊变量中报告了该失败。与通常一样,程序能检查或忽略它,并把从例程返回的误用数据当作真实的进行处理。
5)忽视硬件缺陷或错误情况
程序应该假定它能连接的设备是失败的,许多设备能够发送警告某件事情出错的返回信息。如果有设备这样做了,停止尝试与其交互,并向某人或更高级别的控制程序报告该事件。不能忽视该类情况以避免造成不必要的困扰。
6)数据比较
结算银行存折时,你有一个你自己认为的余额数值,银行也会提供一个余额数值。在考虑了服务费,银行利息,最近帐单等等数据之后,两个数据不吻合,那么就要好好查一查了。在互相检查两个数据集或计算结果集时,也会产生类似的情况。
3、错误恢复
程序中存在错误,程序已经检测到了错误,而且现在正设法对其进行处理。许多错误恢复代码只是稍微进行了测试,或者根本没有进行测试。错误恢复例程中的缺陷可能比原始问题更严重。
1)自动错误更正
通过检查其他数据或规则集,有时程序不仅能检测错误,而且还能纠正错误,而用不着麻烦任何人。这样的程序是令人满意的,但仅当这种“纠正”是正确的情况时才是如此。
2)未能报告一个错误
程序应该报告任何检测到的内部错误,即使它能自动纠正错误产生的后果也一样。在稍微不同的环境下,它可能检测不到相同的错误。程序可以向用户报告这一错误,也可以向一个多用户系统的操作员,向磁盘上的日志文件,或者是这些对象的任一组合报告错误。总之,不管怎么样,只要发生了,它就必须报告。[ 40]
3)未能设置一个错误标志
某子程序被调用,但是结果失败,假定它在失败时设置了一个错误标志,它把控制返回给调用程序,却没有对这个标志进行设置,那么调用程序就会把无用数据当作有效数据传回去,这是应坚决避免的状况。这可能会造成数据冗余,脏数据或是直接导致当前操作失效,严重的则会引起崩溃。
4)程序要走向何方?[ 41]
一部分代码失效了。它记录了错误,并设置了一个错误标志,接下来干嘛呢?尤其是经过了几个跳转的情况下,它如何才能得知程序中的什么地方返回了控制?
5)中止错误
停止了程序,或者当它在检测到错误时自动停止了,那么它是否关闭了任何打开的输出文件呢?它是否在关闭时会记录退出的原因呢?在最普通的条件下,在即将结束之前它是否进行了整理或者它只是结束但可能留下一团混乱呢?这都是开发人员和测试人员需要考虑的问题之一。
6)从硬件问题中恢复
程序应该适度地处理硬件故障。如果磁盘或其目录已满,你应该能够放入一张新的磁盘,而不只是关闭了你所有的数据。如果一个设备很长时间还没有准备好接收输入,你就没有应该假定它已经断线或断开连接而进行处理。程序决不能够让我们永远在那里坐等。
7)不能从遗失磁盘中退出
假定你的程序要求你插入一张具有它需要的文件的磁盘,如果插入的磁盘不正确,它会再次提醒你,直到插入了正确的磁盘为止。然而,如果没有正确的磁盘,你就没有任何办法可以退出,除非你重启系统。不,这种做法是极端蛮横的,决不能允许出现这样的问题。
4、边界相关的错误[ 42]
一个边界描述了程序的一个改变点,假定程序在边界的一边以某种方式做所有事,而在边界的另一边,它以不同的方式完成所有事。
边界相对立的两边的典型“东西”就是数据值。存在三种标准边界缺陷:
l 边界情况的处理不当
如果一个程序把任何小于100的两个数相加,不接收任何大于100的数,那么当你恰恰输入100时它会做何反应?它又该怎么做?
l 错误边界
规格说明表示,程序应该把任意两个小于100的数相加,同时不接收大于95的数。
l 边界外情况的错误处理
边界某一边的值是不可能,不可信,不能接收,或是预料之外的,没有为其编写任何处理代码。程序是否成功拒绝了大于100的值?或者是否当它获取了一个大于100的值时就会崩溃?
我们把边界的概念看得更广泛,边界描述了考虑一个程序以及它在其极限周围得行为得方式。存在很多种的极限:最大,最小,最新,最旧,最近,第一个等等。相同类型的缺陷可以伴随其中任何一种极限而产生,我们可以用相同或类似的观点考虑它们。
不同边界错误的考虑方式
不同类型的边界,其考虑方式也是不同的,但是其思想基本上都相近:无外乎上溢出与下溢出。
1)数值边界
有些数值边界是任意的,如大于100;而有些则要描述自然极限,如三角形的特征和子母的ASCII码等。
2)与一个边界相等
在一个列表中的所有元素可能相同,也可能不同。如果你试着对任一列表进行排序,会发生什么?如果列表由数字组成,当你尝试计算平均值,标准偏差,对称系数时又会发生什么?(以上是概要统计概念,按算法,对称系数可能会计算为0或引起被0除的错误。)
3)多种多样的边界
一个输入串可以长达80个字符么?如果你输入79、80或81个字符会如何?程序是否在每种情况下都接收你的输入?一个列表可以只是一个元素么?没有元素可以么?仅含一个数值的标准偏差又是什么呢?
4)空间中的边界
例如,如果一个绘图程序绘制了一个图形,并在其周围绘制了一个方框,那么该如何处理一个应当在方框外正确显示的点?
5)时间的边界
假定程序显示了一个提示,停留60秒等待你回应。然后,如果你没有输入内容就显示菜单,如果正当它开始显示菜单时,你开始输入内容,会发生什么?
假定你在计算机仍然在从磁盘中装入程序时按下空格键,发生了什么事?空格键是被发送给操作系统,为正在装载的程序进行了保存,还是仅仅因为预料之外而导致计算机崩溃?
6)硬件相关边界
如果一台主机可以连接100台终端,那么当你加入99,100,101台时会发生什么?如果你让100台终端同时登陆会怎样?
如果磁盘已经塞满了会如何?如果一个目录能保存1000个文件,当你尝试保存第999、1000、1001个的时候会发生什么?如果打印机有较大的输入缓冲区,当你的数据填满缓冲区,但是却还没有更多数据要传递时,会发生什么?当打印机缺纸或颜料用完了又会发生什么?
5、计算错误
程序计算一个数据得到了一个错误的结果。发生计算错误通常因为下面三种类型的原因:
l 很差的逻辑:
可能存在一个录入错误,开发人员可能会在编制程序时无意中错误简化了复杂关系地表达式,或者由于拼写错误或笔误导致。另外一种糟糕的情况则是设计错误,开发人员关于代码如何做的概念可能一开始就错了。
l 很差的算法:
如果1+1=1,可以理解为一种特殊逻辑,但是如果把它作为数值运算,恐怕是没有人会同意的。无论何时,一个错误的算法总不会得出正确的结果。
l 不精确计算:
由于使用了舍入的计算方法,很可能在计算时丢失精度。
小结
这篇文章最早成形于2004年10月,当时正接手一个项目的黑盒测试工作。然而,在对软件项目实施黑盒测试的过程中,的确也看到了很多值得思考的地方。作为一名刚进入测试行业的学步小儿,也谈不上有什么丰富的经验累积,唯希冀本文能给刚进入测试领域的同仁提供了一些有价值的参考。