如何有效地与开发人员一起工作

如何有效地与开发人员一起工作

 

原文:Working Effectively With Developers - Brian Marick

(注:本译文最早发表在51testing的第7期杂志上)

开发人员与测试人员通常不能很有效地工作在一起。开发人员能改变,我们也能改变,大家都能改变。我未能幸运地直接改变其他人的行为。但是我能幸运地改变我自己的行为,从而间接地改进开发人员的行为。而他们的改变又给了我很好的反馈,从而建立起一个有效的循环关系。本文是关于我怎样有效地跟开发人员一起工作的方法介绍。

 

我希望我的读者,作为测试人员,花大部分的时间在寻找bug上,还有跟开发人员讨论,写bug报告。

 

本文介绍我这些年来学到的三个能把事情做得更好的方法。

1、  定义自己的角色,让开发人员觉得他们需要我在旁边帮助他们。我帮助开发人员在bug还没出现之前就把它消除掉,从而减少项目总体成本。

 

2、  给感到疑惑的开发人员解释我的工作。减少对我做出武断的评价和看法的机会,我会使用与众不同的方法来让我的开发人员相信:bug报告不应该被看作是威胁。

 

3、  尽量减少会产生误会和曲解的bug报告

 

这三部分很大程度上是独立的,方法和手段不互相依赖。

 

一、选择一个有效的角色

在这一节,我首先描述一下我喜欢的角色和这个角色的日常工作。然后描述这个角色解决的问题,最重要的是,可能产生的新问题。

 

假设你被告知要测试某个开发人员的工作,也许是增加了一个新特性到产品中。你也许要同时测试多个开发人员的程序,但是我会在后面的章节覆盖这些复杂的情况。我假设你会在编码阶段开始工作:在开发人员开始写第一行代码后,但在它被完成之前(除了修改bug的情况外)。如果你在更早的阶段介入,那会更好,但是我不假设那种情况。如果你在代码完成后才开始进入你的工作,那么这个章节的作用除了帮助说服你承认下次应该早点介入外,就作用不大了。它也不能应用在那些你不会去找一个开发人员的工作的bug的地方,例如配置测试、压力测试或其它类型的测试

 

测试组是为谁服务的?一个普遍的回答是“顾客”。在这种模型下,测试人员站在顾客那边,为顾客服务,保护顾客的利益。顾客是一个无助的少女,被绑在铁轨上,一辆失控的火车(处于失控状态的产品开发)冲向她。只有测试人员才能救她,阻止火车(阻止产品发布 - 至少要到bug都修改完)。

 

这个模型有很多问题,不仅仅是这个模型几乎是不工作、不生效的。火车往往冲向少女,让她的救世主失落,士气低下、愤世嫉俗、工作效率低下[Marick97a]。然而对于这篇文章的目的而言,有一个问题是非常相关的:把人们想象成坏人的角色,对于想寻求他们帮助来说不是一个好的开端。不要误会:你其实是要花很多时间从开发人员那里寻求帮助的。你需要他们解释程序,这其实是在要求开发人员给你最宝贵的礼物:时间。你会登记bug并且希望开发人员聪明地处理它们,而不是寻找各种理由来宣布某个bug是一个特征而已,要求有更多的信息,或者干脆开始浪费你的最宝贵的资源:时间。

 

也许我有点夸张。也许没有开发人员会真正相信你在把他们想象成坏人。大概他们会相信某些更糟糕的事情:你把他们看成会频繁出错的人以致不可信赖。大部分开发人员会被认为是坏人而不是缺乏能力的人。但是不管反应的差别有多小,事实是这个模型给了开发人员和测试人员一个不一致的目标。开发人员希望早点发布;测试人员尝试阻止他们。开发人员的合作顶多是一种处于责任感的态度,甚至可能是不满的抵触态度:不可能达到让测试人员融合到项目组中的境界。

 

所以,如果不是为顾客,那么测试组应该为谁服务?

 

我认为应该是为那位对项目的所有事情负责任,并决定产品是否能发布的人而服务。“所有事情”包括了:了解竞争者在做什么;知道程序员因为疲倦而存在的风险,不能进一步提供好的程序,而是提供很糟糕的代码;清楚承诺没有实现带来的代价,产品延期发布给公司和顾客带来的损失。(见[Bach97],会有更详细的讨论。)我把那个人叫做“项目经理”。测试组的工作是为项目经理提供其中一项精确的评估:产品的缺陷率(这个产品的bug多不多),主要是从顾客可能看到并关注的角度去看。(关于这个论点的细节,参见[Marick98a]。关于测试组如何报告缺陷数据和其它类型的需要跟踪的信息,参见[Marick97b]。)

 

让我强调一点,测试人员仍然做的是相同的核心工作产品:bug报告。只是这些bug报告的使用方式不同。不是作为针对和指责产品的证据,而是作为提供给项目经理的信息,以便他做出更好的决定。

 

现在应该很清晰了,这个新的角色定位帮助你跟开发人员一起合作。从他们的角度看看:测试人员就在身边,不断尝试找出可能存在的问题,时刻准备着扑向bug并把它交给项目经理。当然没有人喜欢告密者或搬弄是非的人。

 

那么如何克服这些问题呢?一个解决办法是一个叫egoless programming(无私编程)的项目组结构[Weinberg71],在这种团队里程序员不认为代码是他们自己的扩展,他们不自私,所以他们自己的错误对于他们来说没有那么多的威胁。然而,我不赞成这种方法,因为这意味着开发人员需要改变他们的行为,而不是我要改变。跟我一起工作的开发人员很少有无私的,但是我没有其它的选择,只能跟他们一起工作,接受这个现实的局限性。

 

我的解决办法是转移到private bugs(私有缺陷)和public bugs(公有缺陷)的差异上。私有缺陷是程序员产生的bug,但在把代码签入到公共源代码库之前修正了,它可能会对他的程序员同伴们造成潜在的危害,或者对后面的顾客造成影响。但是一旦bug已经出现,为了保护同伴,要求它必须被公开。(注意这个重要的区别:它不是为了处罚某个制造这个bug的人而公布,而是为了保护其他同伴。)

 

我作为一名测试人员给这些开发人员带来的信息是:我是来帮助他们把bug阻止在private阶段,防止他们出现在public阶段。转眼间,我变成了他们的盟友,成为专注于帮助他们变得更好的人。我的工作就是像他们的一张完全网一样地服务,专注于那些他们不能避免或自己找出来的bug。我是一个工具,作为开发人员的扩展(不要忘记,即使我们与开发人员紧密地工作在一起,他们其实通常都会自己找到更多的bug,比我们找到的要多。)

 

让我澄清一些东西。假设我的开发人员准备好了一块新的代码并且修复了3bug。我会马上开始测试。唯一会使我推迟测试的是我已经在为另外一个开发人员在做相同的事情。

 

当我找到一个问题的时候,我会发Email给他。我通常不会把那些bug登记到公共的bug跟踪数据库中。我不会把它们跟其他测试员、其他开发人员、项目经理一起分享。它们是私有的。我用自己的Email目录记录并跟踪它们,除非开发人员要求我把它们公开地跟踪起来。

 

开发人员可能不能在把代码放到公共源代码库之前修改所有bug。我一般不同意这种决定,但是我接受开发人员的决定。(这跟我的关于测试组与项目经理的关系的观点是一致的。)如果他不修改bug,则我会把它从Email目录移到bug跟踪系统。因为现在这个bug是公开的,公众(项目经理、其它开发人员)需要知道它的存在。

 

我如果不是在找私有的bug,就是在找公开的bug。也就是说,我完成了测试的代码已经放到公共源代码库了。(也许它已经放到上面了,但是我没能及早分配到这个测试任务。或者是程序员需要让某些内容独立工作,出于演示demo的需要或出于让其它程序员的工作能正常进行的目的。)我在那里找到的bug是公开的并且会报告到公共的bug跟踪系统中去。

 

这样说有点过于简单化了,目的是想说明我的观点:帮助程序员寻找私有的bug。在现实中,事情会更加复杂点。假设刚刚完成的代码不是非常重要,但是代码中的bug已经是public的可能对其他程序员造成严重的阻碍。那么我可能会专注于publicbug。我总能得到开发人员的支持,原因在下个大章节解释。有时候,在项目结束之前,我需要确保所有代码都得到了充分的测试。稳定的变更的测试也不能阻止我这样做。

 

现在什么问题变小了?

为什么我要这么麻烦呢?看起来我是想去巴结一些朋友。朋友是好的,但是公司不会为我的社交生活付钱。公司给我报酬是让我使用一部分权力来达到某些目的,一种减少问题的方法。什么问题?

 

一般而言,摩擦。

我遵照John Daly的原则(注:Noel Nyman告诉我的。得到了John Daly本人的许可),不断地问自己:“我做测试不是找bug是做什么?”摩擦会减缓进度。开发人员和测试人员的一些典型摩擦浪费的时间其实可以更好地用在找bug上。

 

我的这种方法还帮助解决其它的问题。

 

Bug的成本高、找得太迟

如果一个bug能尽早发现,总是会比等到开发人员已经忘记了这个问题相关的内容时再修改的成本要低、修改速度要更快些。同时也可以避免正式的bug跟踪数据库的管理成本。

 

开发人员不测试。

对开发人员普遍的抱怨是他们根本不会自己尝试一下就“把最新的build版本扔过墙来”。一测试就停掉了,因此是不可测的,它们又被扔回去。没有意义的工作:浪费时间。

 

一个类似的问题是开发人员声称一个bug已经被修正了,但是最简单的测试就能揭示bug仍然存在。我曾经见过一个程序员把一个6行代码的函数重复修改了4次,每次都会引入一个明显的问题。明显看到他根本没有对自己修改的程序进行任何的尝试。

 

看起来我把问题弄得更糟糕了,因为我让测试人员把这个程序员束缚住了,每次修改都及时进行测试。是否我没有给开发人员足够的时间测试?不是的,因为我改变了这里的经济学。在这之前,程序员如果节省了10分钟但是浪费了你两个小时,那不会对他造成任何损失。但是现在会了:那两个小时的浪费意味着更多的privatebug会变成publicbug。他会有更多的积极性去跑那些你为他构建的自动验证测试(“冒烟测试”),在把代码交给你之前他会尝试测试新加的功能和修改的bug。他会跟你讨论怎样的分工是有效的。

 

“请为我debug一下这个问题”

开发人员通常要求测试人员为他们做很多debug的工作。他们会要求具体的测试用例即使是只有几个有限的步骤。他们会要求要对不同输入的测试的扩展文档即使是他们可以自己有效地尝试进行。

 

同样地,这个问题也得到了减轻,因为浪费你的时间也会使程序员付出一些东西。更进一步地,因为反馈的循环很紧凑 你只需要给开发人员展示错误是什么,而不需要填写详细的bug报告,而这些报告可能要等上3周的时间开发人员才会看到 你为开发人员debug的成本降低了。

 

(译者注:我认为这里作者的debug的意思不是指我们通常认为的程序的调试,而是测试人员为了重现问题而做的操作。)

 

不知道什么是最新的。

测试员通常会抱怨他们不知道什么是新的编译版本中的内容。所以他们不清楚应该测试什么东西。有时候我们没有考虑到这点:对于每个东西都及时更新并通知每个人实在是太麻烦了。有时候则是故意的:开发人员迫切希望增加这个新的特性,但是没能得到批准,然而他们听了Grace Murray Hopper的演讲和她的口号“得到宽恕比得到许可更容易”,所以增加了新特性。(注:海军少将Grace Murray Hopper,已故,在计算机的早期阶段。她被认为是第一个发现计算机bug的人,发现一个蛾虫躺在 Mark I 计算机里。(有些人对这个看法提出一些质疑。))

 

对于一个与你一起工作的人很难会欠缺考虑的,尤其是他是帮助你把问题阻止在新特性处于private的状态时。同样你也很难对这样一个人隐瞒你的恶作剧。

 

“停止要求那些愚蠢的文档。”

测试人员在判断产品有没有做它要做的事情之前,需要知道产品应该做的是什么。程序员不会很乐意提供那些文档。

 

当工作在一起的时候,更多的信息能被非正式地流转。那也许不是很理想,因为比你后来的测试员还是什么都不知道,但是比什么都没有要好。(注:顾客也会什么也不知道,但是现在一般人认为他们通过尝试系统的功能特性来学习和了解,而不是通过阅读测试人员需要的详细的文档。)作为测试员,我也通过自己编写文档来贡献自己的价值(把非正式的信息转成正式的信息)。(这与Coplien的唯利是图分析者模式类似[Coplien95]。)

 

“请做这些不是你的工作的事情。”

测试组看起来在收集令人讨厌的、困难的、往往阻止它们做真正的测试的工作。尤其是测试组被冠以QA的头衔时,一个很模糊的术语,任何不想维护配置管理系统或创建系统构建版本的人都会同意QA应该做这些事情。(注:确实,这里我为了效果而夸大了。我作为“构建独裁者”超过了一年的时间,我没有发疯,最少在一开始的时候没有。)测试人员帮助开发人员可能导致方向的迷失,例如,编写一些辅助的代码。

 

你从我的技术作者的经验可能已经猜想到我不反对测试人员帮助项目组。但是,一旦bug修改成为开发人员的例行仪式,John Daly的原则会更让人着迷。

 

不可测试的代码。

测试人员通常会碰到很多代码的问题是很难重现的,但是用户可能不小心就暴露了这个问题。或者他们不清楚自己做了什么操作。或者不清楚代码究竟做了什么事情。当开发人员重视你的bug的时候,他们更倾向于添加增强可测试性的辅助代码。通常一点点就能帮了大忙([Marick95],第13节)。

 

开发人员缺乏对bug的反省。

当我与开发人员紧密地工作在一起的时候,他们学到了我检测bug的技巧。他们会更加有能力防止那些我发现的bug的再次出现。这种方式下,我可以“顺流而上”,帮助开发人员设计和计划而不是仅仅对于已成事实的软件进行测试。我不会把这些东西推给开发人员;我会等他们来问。

 

我可能会引发什么问题?

每个解决方案都可能会带来潜在的新问题。

 

你必须要早点开始

如果你等到代码都写好了并放到公共的地方了才开始你与开发人员的工作,则剩下的就只有bug的修改了,你不能得到很大的益处。

 

引发对测试人员和程序员的比例的关注

考虑我的这种方法与“扔过墙”的测试方法的区别。

 

l         更多的中断驱动。为了维持好的关系,当程序员需要你的帮助时你要快速地反应。在传统的测试中,你拥有更多的自由去安排你的测试任务。

 

l         更加依赖结构的知识。在两种类型的测试中,你都必须从用户的角度看一个功能特性。在传统测试中,对功能特性的结构或整个系统的架构的理解会对你的测试有所帮助;它加强了你的以用户为中心的测试,并且有时候允许你忽略某些不必要的测试。但是,当你与开发人员紧密地工作在一起的时候,这些东西的理解就会更加有用:它们是有效沟通的关键并且 也同样重要地 让开发人员感觉到有效的沟通。

 

这里我需要小心说明。我不认为你需要成为一名程序员才能了解关于结构的知识。我曾经看到过很多非程序员能把这些学得很好。我曾经看到一个很棒的程序员高度赞扬一名非程序员因为解释结构给他们听需要他阐明他的思想。

 

这些区别意味着什么呢?

 

假设测试人员与开发人员的比例是110,而你尝试跟每个开发人员都紧密地工作在一起。

 

l         一般请求帮助会以不可预知的间隔发生。你需要安排好帮助他们的顺序。但是那自然会降低你对某些人的价值,所以下次他们就不会那么愿意叫你帮忙了。

 

l         你不能跟上每个程序员的子系统的结构。你会需要程序员解释一些东西,而这些东西在他们看来你应该早就知道了。你可能需要他们再次解释某些东西,而这可能会激怒程序员。(在他看来,他刚刚给你解释完。从你这边看来,介于7个不同的任务之间,很容易把刚才解释过的东西忘记了。)或者,更糟糕的是,你可能记住的一些东西不再是有效的,导致错误的测试或误报的bug

 

在这种情况下,测试会退化成“扔过墙的测试”,因为在这种局限下“扔过墙的测试”会工作得更好些。为了避免不好的感觉,你也许倒不如就按这种方式测试更好些。

 

假设比例是13左右。则容易处理些,但是仍然可能会让开发人员失望,因为不能像他想的那样测试(意味着太多privatebug被忽略了)。你需要谨慎地处理这些问题。注意确保不要过分承诺,导致不能兑现期望。我曾经那样做过。很不好,比不紧密工作在一起还要糟糕。要尤其注意识别出开发人员代码的风险区域,或者高风险类型的bug,明确说明那些是你将要重点关注的区域。

 

因为我知道开发人员很容易寄希望于测试人员,我更喜欢被指定与一名开发人员紧密工作在一起,而同时用传统的方式兼顾其他两名开发人员。

 

你不能从秘诀中学习

假设测试人员和开发人员干得很漂亮,很少bug出现在公共源代码。这样的话谁还能从这些少得可怜不值一提的公共bug报告中学到东西呢?Noel Nyman读了本文的初稿后写到:“当我到了一个新的测试组时,首要的信息源就是bug历史。它是洞悉产品的无价之宝,并且是制定回归测试内容的依据。没有谁告诉我关于产品的信息能超过我从旧bug中发现的信息的10%。”其他评论者也指出,开发人员可能学着避免他们自己独特类型的bug,但是他们不能从看其他开发人员制造的其他类型的bug中学到东西。过程改进组也会有类似的问题。

 

对于这个问题我没有很好的回答,除了作为一个我愿意付出的代价外别无它法。公共的bug还是会被发现并登记。在某种意义上,它们是最好的能学习的东西,因为它们逃脱了开发人员和测试人员的眼睛,但是了解私有的bug还是会更好些。成功实施过程改进的公司(我从未在这样的公司工作过)可能会更加无私一些,在那里开发人员会更加愿意把privatebug登记进去,为了公众着想。(有两个评论者Johanna RothmanJeanine Brown曾经看到开发人员是这样做的。)我在下一主要章节的论点会帮助我们朝那个方向前进。我没能有幸在一家公司是能很好地做评审的,但是这样的公司可能已经习惯于共享privatebug,至少在开发人员之间。

 

控制Build

我曾经把这样的方法应用到相对单片集成的和自我包含的产品中:我从开发人员手中获取可执行程序然后在我的机器上执行。如果产品被分解成很多块(共享库、配置文件等。)并且开发人员不负责构建和整体地提交,或者没有实用的方法可以单独测试开发人员的每个模块,知道它是真的孤立的,跟其它模块是隔离的(尤其是包含其他开发人员在同时开发的模块时) - 那么你就碰到了配置控制的难题了,它可能吞没紧密工作带来的好处。最好能定期获取完整的build版本。

 

设置了错误的期望值

开发人员可能会期望获得很好的益处但是不付出什么代价。应该小心设置他们的期望值。

l         你的工作是提供关于bug的早期信息。那就意味着所有的任务 项目经理认为有足够的质量的代码创建 要快速地进行[Maguire94]。然而,如果开发人员修正bug时你在找bug,那么任务的第一部分 获得第一个可演示的代码版本并放到源代码控制中 可能会延迟。开发人员可能会认为那很恼人或不合适。

 

l         虽然你会小心的保证他的时间,但是你还是需要知道很多信息。你需要跟他讨论,或给他发送Email。这些都会占据他的时间,不可避免地使他觉得受到打扰。承诺尽量减少中断他的工作。例如,为了充分利用他的非工作时间,我曾经在开发人员吃中午饭、早餐、晚饭的时候访问他,或者在送他到机场的时候或取修理店取他的车时。但是有些时候打扰还是避免不了的。

 

l         确保你强调了你们的关系会不可避免地引发一些摩擦。你会烦人地在他认为他已经完成后发现一些严重的bug,让他感觉你为什么不能一开始就发现这些问题。他最好抱怨,即使是不恰当的抱怨,也好过“避而不理”(作为一种逃避不愉快的问题或人的方法而消失)。避而不理不是解决的办法,因为你会坚持。虽然是客气的坚持,但是还是坚持着。(我曾见这样说“我还是会坚持帮助你及时你再也不能忍受。”)

 

合作可能会失败

紧密的合作关系是对时间的投资。有时候投资免不了得不到回报:

 

l         你的程序员是如此的固执以致你尖叫起来 只可惜很可能你的尖叫声还没他尖叫着说你固执来得响亮。

 

l         程序员可能会看起来故意阻碍或令人误解。(他也许在尝试通过使用公正的手段或不正当的手段来指示你从而节省他的时间。但是有时候他就是不可避免地粗心大意,或尝试隐藏他的无能,或其他什么原因。)

 

l         你的期望值没有达到。程序员对你做的事情不高兴。

 

我个人倾向于向糟糕的投资倾注更多的精力,更多的时间。那是错误的。有时候你必须承认计划失败并转向另外一个能工作得更好的计划。在这种情况下,后备计划是“扔过墙的测试”:撤退,保持一定的距离来工作,更正式的工作,专注于公共源代码的重大版本的测试。

 

因为我容易陷入一个角色,我发现清晰地评估合作关系的状态很重要,尤其是在早期。所以我记录下我花在跟开发人员沟通上的时间。有时候我会问自己是否值得投入这么多时间。沟通的人的性格如何?

 

l         是花在回答特定的问题上还是花在解释某个观点?或者是关于普遍性的问题?(当我与开发人员纠缠在一起的时候,我发现我会写很长的关于软件测试的基本原则的Email来解释我的决定。)

 

l         我是否发现自己很小心地写Email来避免任何可能的误解,或者在我跟开发人员讨论之前要很仔细地准备自己的言辞?那些是应付低效率的谈话的前期准备,但是它们本身会花费很多的时间。

 

l         我在重复自己的论点吗?开发人员呢?

 

l         有些低效率是一开始不可避免的。但是后面有没有减少?是否减少得足够快?

 

我也会问自己从这些得到了什么。

 

l         我学到的关于代码的东西能帮助我的测试吗?我忘记了什么好的注意没有?

 

l         有什么bug是比我预期的要早发现的?

 

l         我是否花费更少的时间在写东西、讨论、重新看bug报告上?

 

l         我改进效率的机会是什么?

 

当我受到挫折的时候,我会在我暂时离开工作一周后问自己这些问题。然后我平衡一下成本和效益。如果我怀疑我能达到我需要的结果,我会短时间尝试一下新的沟通方式,然后重新评估。或者我会简单地撤退。如果让我陷入的只是一个测试任务,我仅会从那里退出而已。

 

我不会因此而责怪谁。如果需要受到责备,我会乐于自己接受,有两个原因。第一,已经有很长时间被浪费掉而没有发现bug了,为什么还要花费更多的时间?第二,我确信,我不是足够的好,我不能跟任何开发人员一起有效的工作。

 

虽然没有必要大事宣扬你的计划改变的原因,但是确保你清楚地跟开发人员沟通,你的管理者,还有开发人员的管理者。他们有权利知道,不告诉他们会把问题弄得更糟糕。

 

你是明星吗?

一个评论者这样写到:“从我的角度看来,你的建议是让我找机会使得跟我一起工作的开发人员看起来很优秀而我看起来是什么都没做我碰到的每个测试经理都高唱着bug数量不是有效衡量测试员的效率的标准的口号。但是真正我为他工作的每个人,在某种程度上,都在使用bug数量来挑选、奖励、提拔测试员。”

 

这个对于我来说不成问题。不管是为了什么原因,我从来没有意识到经理在使用bug数量(除非在某种含糊的意义上-像印象之类的)来评价我。如果你的经理这样做了,那我的方法就是一个问题。为了满足开发人员让你牺牲你的工作会有点过分。取样的方式可能会生效:假设你与4个开发人员一起工作,一个是比较紧密的,而另外3个是用传统方式的。你的经理可能会认为你在这3个开发人员身上发现的bug数量是你全部工作的代表。(这样的话就取决于你再他们的代码上测试花费的时间,但是这样简单的衡量就像“玩数字游戏的赌博”一样。)用开发人员方面获得证明,尤其是以请求你的服务的方式,会产生奇妙的效果。

 

个性和经验

有些测试员喜欢协作完成工作;有些则是不合群的。我的这种方法会减少你与其他测试员的交流。你的经理会看不大清楚你在做的事情,如果你没有意识到这一点则会很糟糕。这种方法给没有经验的测试员增加了风险。

 

有些测试员喜欢制定计划然后执行它。有些则比较能容忍过程的中断。我的这种方法倾向于后者。除了要能容忍,你还必须是能自我组织良好地完成你的所有工作。

 

斯德哥尔摩综合症

“斯德哥尔摩综合症”指的是与俘虏联结在一起并且采纳他们的观点的倾向。例如,人质可能成为敌人去观察警察。(注:http://www.vswap.com/confuser/stockhol.htm上有一个很好的简短的描述,但是当你去看的时候也许已经不见了。)

 

把测试员看成是程序员的俘虏有点过于戏剧化。但是长期与开发人员在一起工作,你可能采纳他们的观点而放弃了顾客的观点,导致你遗漏了bug。尤其是你可能遗漏这样的bug:产品决定这样做的并且是这样做的,但是却是顾客不需要的。可用性问题是这类问题的一个例子。一个科学计算器提供以2为基数的对数计算(主要是对程序员来说有用),而不提供大部分用户期望的自然对数的计算,或者一个在线银行系统给用户默认设置与用户名相同的密码。(一个随机的密码会更好,因为很多用户从来不修改他们的密码。)

 

所以我们要权衡好紧密工作的风险和价值。在大部分项目中,大部分时间里,我相信价值大于风险。“大部分”不是指全部。在安全至上的项目中,如果妨碍测试员吸取程序员关于潜在错误模式的假设,那么带来的附加的成本需要这些独立的测试组来自己承担。

 

尝试把顾客或其他用户的模型明确下来会减少这种感染的风险。下面这些书教会我如何在跟开发人员讨论时心里想着顾客:[Moore91],一本关于市场的书,描述塑造目标顾客特性的过程;[Cusumano95],描述基于行为的计划;[Gause89],一个主流的描述如何挖掘需求的软件工程的书;[Hohmann97],提倡用完美的将来时态描述用户行为。(除了[Gause89],这本书写的很多是与测试不相关的其它东西。)

 

测试组的一部分应该专注于整个产品的测试,而不是测试孤立的功能特性。这样的测试通常是基于工作流来组织的(用例、场景、任务),最好由领域专家(例如,会计师来测试会计模块的内容)来测试。这些独立的测试人员来寻找那些因为受开发人员感染的测试人员遗漏的bug(还有功能模块交叉区域的bug和可用性问题)。

 

二、解释你自己和你的工作

不管你的角色是什么,你都必须正确地展现自己,这样才能使开发人员对你的看法是你想得到的。

 

这个章节不假设你使用前一个章节的方法。它适用于所有你需要给开发人员解释测试的情况下。

 

良好的第一印象

测试人员通常被开发人员武断的评价所伤害。一个很普遍的情况是测试员想要成为程序员,但是开发能力没有达到。所以你的第一个任务是避免太快地被归类了。这里有3种方法。

 

永远不要过分夸大你的编程或技术能力

我曾经参加一个会议,会议上一个经理夸夸其谈“我们在1971年就开始了面向对象编程;我们只是没有这样叫它而已”。虽然他在某种意义上是正确的,但是他从那一刻开始失去了他的听众而且永远也找不回来了。

 

他试图发出的信息是“我是技术型的,就像你一样。技术上的指导可以听我的。”但是程序员接收到的信息是“我想我比你知道得更多。我不需要进一步的学习。我会在痛苦的培训中浪费你的时间,或者让你做愚蠢的事情。”

 

测试人员夸大他们的编程技能发送的也是同样的讯息。

 

忽略你自己的技术能力会好一点。很多程序员希望你是这样的。400多年后,他们回应了Castiglione的关于完美文艺复兴追捧者的描述:“少讲点话,多做点事,对于值得赞扬的事不要赞扬自己,假装不知道[Castiglione28]。要警惕任何想吹牛一样的话。

 

在某些偏门的技术区域拥有专长会很有用

但是不吹嘘一下又不足以建立自己的威望。转移是一个好方法。假设你是恰好是公元前第五世纪的希腊有桨船的专家。程序员也会尊敬你,因为他们普遍对所有专家都尊重。在一些古怪的主题上成为专家,与编程没有什么关系的方面,比在同行领域有专长要更有价值。对你的这些业余专长的尊敬会转移到对你的测试能力的尊敬。我不知道原因,但是很多很棒的程序员也拥有令人惊讶的、与本专业完全不相关的专长。(注:有一个技术专长是很容易用于转移的:测试人员如果非常了解硬件,能很快找出打印机的问题,或者为什么一个以太网卡不工作了,或者能凭记忆配置任何调制调解器。这些问题可能发生在开发人员的机器上。这些是与编程很接近的专长,但是提高了测试人员的形象。)

 

但是,还是要小心点。我拥有英语文学学位,给了我写文章时使其变得适当的有趣和旁征博引的能力背景。但是我宣称是这方面的专家,则会带来危险和错误。我拥有学士学位只是培训的一个证明而已。如果我尝试把它当成专长,则是冒险陷入某些人的视线中。

 

置身层次地位之外

我的英文学位不会让我成为专家,但是在其它方面是有用的。如果我是博士学位,但是计算机方面只有学士学位。那么如果在证书展示的场合,我的位置就很明显了 会比我想象的低。但是,如果我不提我的计算机学位,而是描述我的英文主修课程,那么突然我就被归类了。我还获得了作为交叉学科人员的威望,专家在这面前感到一点心虚是好事。(因为对英文主修存在普遍的偏见,所以这需要我展示一些技术性的能力后才会生效。)

 

测试员可以简单地避免被归类。如果你有绿色的头发,几乎每天晚上跟摇滚乐队一起玩音乐,则程序员很难把你归类为“想成为程序员的测试员”。

 

解释你的工作

然而,我不推荐你把头发染成绿色,那样不够直接。在某些时候,你必须直接让程序员信服你的价值。在这一节我会介绍一种基于程序员民族心理学的方法。民族心理学是一般人们解释为什么大家做某样特定的事情。它是基于人们有信仰和愿望,基于信仰而行动,从而实现他们的愿望。它不同于存在心理学,存在心理学的目的是发现信仰和愿望是什么,他们产生的原因和导致行动的原理是什么。也不同于流行心理学,因为它不是用来销售杂志或杂志上的广告产品。

 

对于很多程序员来说,支配他们意愿的是他们的同伴程序员。他们相信要达到他们的愿望需要下面几点特征:

l         创造简明强大的抽象概念的能力。这与数学上或物理上的“优雅”的概念比较类似。环境因素可能阻止一个好的程序员创造优雅的代码,但是能力和愿望必须被认知道。

 

l         掌握很多相关技术的细节的能力。参见[Raymond96]的“A Portrait of J.Random Hacker”,在里面,Eric Raymond描述为了写一本书如何快速理解一种固定类型语言的帮助手册。(这与前面的有些关联,让程序员看起来是个通才和专才的结合。)

 

l         很强的问题解决能力,包括把那些抽象概念的逻辑推理能力应用到技术细节中。

 

l         高效率,由代码生产数量衡量。不发布的东西不像发布的东西那样有效衡量(例如,设计文档)。

 

作为传奇人物的程序员会在面临很多一团糟的问题时,弄清楚问题需要掌握的细节,跟踪问题到核心代码中,排除不清晰的地方,然后想出简单的操作就能使复杂的问题得以隔离和解决,向人们展示所有这些细节问题都可以用恰当的操作的组合来处理,并在一周内产生可工作的系统。

 

测试人员则会对程序员的自我形象造成威胁,他们会打击程序员的那些特征。他们会展示给人们看到那些抽象概念没有用,细节没有被掌握好,或者是问题还没被解决。这一点也不奇怪,然后,程序员往往会把测试员的注意力从那些基础的概念转移出去,把它看成是对他们写的代码的系统的探索,寻找代码错误。代码错误不是什么大问题。一个对代码错误不重视的程序员可能会失去一些威望,但是仍然可能会被认为是优秀的。程序员可能从来不制造代码错误,但是创建笨拙的抽象概念。

 

现在,对于测试人员而言,寻找代码错误只是工作的一小部分。概念上的错误,例如不恰当的抽象或错误的假设,更加重要。它们会需要付出更多的代价来修正。

 

这种期望值的不一致会导致强烈的冲突。程序员会认为你在做错误的工作,使用错误的方法。那么现在是时候发现和解释正确的做法的时候了。程序员是无情的问题解决者。(我的妻子,她是个兽医,在见过我的程序员之前对程序员不了解,后来她对我说:“你知道吗?你的朋友对任何事情都有强烈的主张和看法。”)

 

通过给程序员解释你的工作来避免这些问题。解释可以分成几部分。

1、  解释你的工作与他的工作之间的区别。

 

2、  表明你拥有程序员不大可能有的技能,完全可以从第一点的原则推理出来。

 

3、  展示你锻炼出来的那些技能是如何帮助程序员实现他们的完整性的,而不要让他们质疑这些能力。你以不可或缺的方式弥补了控制和驾驭这个世界的能力。

 

让我以一个特定的例子来描述。这是我在写这篇文章的时候想到的,所以只是在一个开发人员身上试过,但是我想这是我发现的最好的描述我的工作的方法。这里是一个谜题:

 

那个人的父亲是我的父亲的儿子,

但是我没有任何兄弟姐妹。

 

这使人迷惑,因为第一行文字描述了下面的情形:

但是这张图与第二行文字描述的相矛盾,因为我的父亲没有其他儿子。

 

这个谜题很容易解决,如果你意识到文字暗示的“那个人的父亲”除了我之外别无他人。如果是同一个人,你就可以画出下面的图,这与第二行文字不矛盾:

 

接下来,我会期待任何程序员能快速地解决这个谜题 毕竟,他们是问题的解决者。但是问题的关键是某人必须在谜题被解决之前创造出谜题。而问题创建的技能是与问题解决的技能不一样的。在这个例子里,谜题的创建者需要把两个情况搅和在一起,知道父亲不涉及他们自己的儿子或者他们自己不一定是那个人的父亲。技巧在于知道如何误导谜题的解决者。

 

这个例子与软件测试关系挺大的,因为测试人员要做的其中一个事情就是去考虑是否两个不同的名字会跟同一样东西关联。(“如果输入数据的文件跟输出的log文件是一样的呢?代码是否会在重写文件之前读一下?”)

 

现在,程序员可能会跳出来说,我们也能考虑到这样的情况。他们是通过修改bug学到的。然而,测试员比开发人员更能找出问题来:

l         这是他们唯一要做的事情。他们看到过很多的bug。他们关于bug的思考会更多。他们花更多的时间在形成问题上。

 

l         就像谜题的创建者很清楚谜题的解决者一样,测试人员研究程序员目的是为了洞悉程序员可能忽略的问题的类型。程序员很难考虑到他们不深入思考的问题。(注:要想研究好程序员,你需要成为一名好的观察者。我推荐大家看[Weinberg85][Weinberg93]。学习其他人已经观察到的东西,可以参见[Gabriel96][Hohmann97][Lammers86][Levy84][Turkel84][Weinberg71][Weizenbaum76]。)

 

l         测试人员还会研究用户,特别在清楚用户知道什么、真实的用户会做什么可能的操作方面。程序员很难去做这些东西。他们可能没有这么多的时间。他们过多地陷入他们的解决方案里,而不会把自己置身于用户的角度。(注:在斯德哥尔摩综合症那一节,我提到一些书可能帮助你学习更多关于你的独特产品的用户:[Moore91][Cusumano95][Gause89][Hohmann97]。我没有读关于大众用户的很多书,但是我可以推荐[Cooper95][Norman90][Nielsen93]。)

 

因此,测试人员做的事情是通过呈现某些特定的细节(以测试用例的方式)帮助程序员掌握相关的技术细节,而这些细节本来很可能不会引起他们的注意。不幸的是,你通常太晚才呈现这些细节(在代码写完后),因此揭示的问题是抽象概念或它们的使用方面的问题。但是那是把测试人员过迟地放到项目中的副作用,也是认为测试人员只是执行测试而不是设计测试的不幸的观念的副作用。如果程序员能及早地了解到细节,问题就不会发生了。

 

程序员帮助测试人员描述他们是怎样测试程序的。这有两个好处。第一,它能帮助测试人员理解程序员的盲点是什么。第二,它照亮了测试人员的盲点 当然测试人员也会忽略一些东西。

 

那是很理性的情况。测试人员通过提供详细的细节给程序员来掌握,从而帮助程序员实现他的真正能力。这是程序员容易接受的论点 至少暂时接受。但是在实践中会碰到障碍,它们很可能是跟bug报告相关的。在第三章将讨论这个主题。

 

你需要解释的关于你自己的其它怪癖

[Pettichord98]包含开发人员与测试人员之间个性的比较。我需要强调其中的3点,因为我看到过它们导致的摩擦。

 

测试人员能容忍冗长乏味的工作任务;程序员则想办法自动化这些工作。

当开发人员转向测试的时候,往往只是专注于自动化测试。(有时候他们花费了很多的时间在自动化的支持上,而不去写任何实际的测试)当程序员看到测试人员手工地运行测试时,尤其是重复地执行相同的测试,关于测试人员能力不强的观点又加强了一些。明确地反对手工测试,指出自动化测试的经济有效,但是他们大概不会去权衡一下。由于篇幅限制,我不能展开讨论;见[Marick98b],让你知道不仅仅需要权衡自动化测试的时间和手工执行N遍测试的时间。

 

测试人员能快速学习;程序员倾向于全面的理解。

每个人都赞赏能快速学习的人,但是程序员可能不会意识到通常测试人员面临的是几乎没有时间去学习一个产品的情况。那诱发了人们“摘挂得比较低的水果”(选择轻而易举的目标去实现)的想法:找出他们需要知道的东西,找到重要的bug,然后继续。因为程序员重视完整性,这样的测试人员看起来是肤浅的而不是有效率的,除非你给他们解释你的工作。

 

测试人员相信无知是重要的;程序员认为专业是重要的。

测试人员知道对于产品构成的无知和所谓“正确使用方法”的无知能让他们发现程序员忽略的东西和用户会看到的东西。比用户更专业其实是危险的事情。问天真的问题能产生令人惊讶的答案。需要让程序员知道“天真”是一种有预谋的策略,不是缺点。

 

三、报告Bug

在这一章节我描述怎样报告bug,从而帮助建立有效的关系。它不要求你使用前两章介绍的方法和技巧。对于更广阔的讨论范围,我推荐[Kaner93][Kaner98]

 

Bug报告是展开你和开发人员之间的关系的一部分。你在报告bug时做两件事:

1、  提供关于程序状态的信息。这是bug报告为人熟知的目的。

 

2、  提供关于你的信息还有程序员能依靠你的程度的信息。

 

一开始,开发人员可能会怀疑。一些程序员有测试人员浪费时间在报告无用的bug的不好经验。有些程序员则听到很多关于测试人员的恐怖故事。你必须让程序员相信你是有用的人,是个值得在身边的人。有3部分内容你可以做。

 

不要浪费他的时间

Bug报告不应该让他来猜测你提供的信息。

l         如果可能,把能重现bug的清晰的操作步骤包括进去。写完后,在提交bug报告之前尝试一下步骤序列。确保你有一个清楚明确描述的开始状态(例如,确保你退出程序并重新启动)。

 

l         把你期待要发生的、实际发生的描述清楚,还有为什么这样不对。

 

l         大部分程序员喜欢获得能在最短的步骤下能重现缺陷的bug报告。有些则不喜欢,因为那没有真正节省他们的时间。(例如,GNU C编译器的缺陷报告方法说明书就清楚地告诉你不要因为需要举个小的例子而怕麻烦。)

 

l         其它也会导致错误的步骤序列能帮助程序员更快地发现错误的原因,但是不要琐碎地描述每个操作序列的差异。那同样会浪费程序员的时间来决定是否有新的东西。(可能会耗费你很多时间去看那些细节从而决定存在的变化是微不足道的。)

 

l         描述一些能成功的场景(令人惊讶地),也会有所帮助

 

l         如果问题在他的机器上不出现,要尽快发现他的机器与你的机器的差异在什么地方。检查bug报告使其反映对配置的依赖。学习产品是如何对配置敏感的。(当然,这对查找配置方面的bug也会有用。在测试的早期,对帮助开发人员扫除障碍很有用。)

 

保持自己置身事外

l         冷酷地把任何隐含个人批评的语句从bug报告中擦掉。你也许需要让一些bug报告“晾”上一个晚上,然后以一种新的眼光来读它。你可能会发现问问题会比做出武断陈述要安全点。(不要忘记问一些诚恳的问题,你需要学习的还有很多。)问一下你自己当发现你认为是bug的问题原来是正确的行为时会有什么感觉。如果你会感觉像个傻瓜,那么同样地,很可能你在bug报告中的某些措辞也是有问题的。

 

l         不要试图解决你报告的问题。你也许没有足够的很好地解决问题的能力。尝试这样做可能会带来反效果。

 

用重要的bug展示你的价值

你和你的程序员之间可能会对什么是重要的持有不同的观点。你可能也像我一样认为可用性问题时很关键的问题。但是程序员可能不这样认为。你如何解决这个冲突?你可能把一些可用性的问题抛给他,希望说服他修改。或者,你首先抛给他那些他认为重要的bugbug是如此重要以至他认为自己被抓住了。然后,慢慢地建立对某人的观点是有道理的这样的信任,你就可以扩展“重要的”范围,把可用性的bug也包括进来。

 

你或许在想我倾向于后面的方法。我曾经保留不是那么重要的bug,等到我获得了程序员的尊重后才把它们报告出来。那样做不容易,尤其是那些bug是程序员也认可,只是认为优先级要低点。假设我被公共汽车撞了怎么办?如果这个程序员被调到其它任务,现在连最小的bug也不能修改怎么办?但是有时候我认为那是能做的最有效的事情。

 

这里是其它一些让他们关注重要的bug的方法。

l         解释为什么对于顾客来说这是个重要的bug。如果你的场景被指责说不切实际,那么想出一个更真实的场景来展示这个bug。如果你的程序员说没有哪个真正的用户会这样做,那么首先仔细地想想他说的有没有道理。他可能是对的。如果不是,收集维护你的观点的好的论据。客服人员是怎样说的?

 

l         很多bug跟踪系统都会有严重级别和优先级别两个字段。严重级别字段描述bug的后果。优先级别字段描述一个bug应该多快地解决。通常,一个严重的bug也是一个高优先级别的bug,但不总是这样的。可能有很好的理由不去修改bug。作为测试员,你大概不会理会那些原因,你也不应该有权利计划一个程序员的时间。因此,当你报告bug时把优先级别字段留空。这样你可以避免把 “重要的”定义成“对于我测试来讲是重要的”。(注:就像任何其他的同事一样,如果你强烈不同意推迟修改bug,你应该考虑进行“斗争”。但是小心地挑选“战场”,在你提出自己的观点之前先让别人做出决定。)

 

如果bug跟踪系统只有严重级别字段,则把它明朗化 用语言或实际行动 你填入的值不代表优先级。并且尝试添加一个优先级字段。

 

l         当你找到一个bug的时候,寻找更多的逻辑结果。例如,我曾经测试一些这样的代码,改变名字用于向产品标识用户自己。为了美观的原因,不能在名字的前面和后面加空格。(因为空格是不可见的,会造成其他用户不知道真正的名字是什么。)我发现代码没有实现这个规则。假设我录入一个bug标题叫“能创建一些用户很难输入的名称”。那么这个bug会很容易地被置为低优先级的。然而,我最近发现用户名是用户本地磁盘的文件名的一部分。我也知道,从以前的bug看到并且从与开发人员的Email看到,产品在与含有空格的文件工作时会有很多问题。这触发了我去尝试使用以空格为结束符的用户名,使用在与磁盘文件交互的地方。也尝试了包含DOS目录分割符‘/’的名称。我会找到更多严重的bug。具体的细节我不大记得了,但是bug报告的标题可以改成“所有过程数据丢失(包含反斜杠符或后面带空格的名称)”。那会更吸引人。

 

随着你跟开发人员和谐地合作,你会发现不需要花费那么多的时间在privatebug报告或Email上。上面的大部分的建议会使bug报告对其他可能的读者更有用:其他测试人员、项目经理、关心开发过程或度量的人。所以你应该继续保持小心地录入bug到公共的bug跟踪系统。

 

总结

那么,最后我们所站的位置是什么?

 

测试人员常常抱怨开发人员是君主。因为那样,测试人员没有得到注意,尊重,他们需要的资源。

 

但是如果你接受这种情况,然后抱着那些隐喻去做你的工作呢?

 

我的观点是开发人员通常是慈善和有用的君主,可以他有他的不可避免的用户知识上的不完整性,还有他自己的错误倾向性。有效的顾问尝试完善这位君主,让他更聪明地看待这个世界,不要让他成为他自己的君主。

 

当然,暴君是需要被打倒的。

 

感谢

我从98年的质量周上获得了这个主题的灵感。可惜我没能取到会议的签到表。所以我只能感谢那些给了我名片的人:Kevin Connery, Reneé Henderson, Nabil Hoyek, Susan Keim, Otto Vinter。我在早期一些人关于这个主题的评论和建议:James Bach, Jon Hagar, Brian Jackson,Mark Johnson, Mark Jones, Monica Lichtenstein, Connie Fleenor Lloyd, Pat McGee,Paula Parash, Pierre Porter, Marcus Porterfield, Linda Rising, Johanna Rothman, Joe Yakich。当我发出这篇文章的修订稿时,我从下面这些人那里得到了格外有用的帮助:James Bach, Jeanine Brown, Kevin Connery, Tim Dyes, W.W. Everett,Peggy Fouts, Nabil Hoyek, Susan Keim, Vince Mehringer, Noel Nyman, Adrienne O'Sullivan, Jeff Payne, Johanna Rothman, Andrew Tinkham, Otto Vinter, Joe Yakich, Ned Young

 

参考书目

[Bach97]

James Bach, "Good Enough Software: Beyond the Buzzword", IEEE Computer (Software

Realities column), August 1997.

[Castiglione28]

Baldassere Castiglione, The Book of the Courtier, 1528.

[Cooper95]

Alan Cooper, About Face: The Essentials of User Interface Design, IDG Books, 1995.

[Coplien95]

James Coplien, "A Development Process Generative Pattern Language", in Pattern Languages of

Program Design, Volume 1, James Coplien and Douglas Schmidt, eds., Addison-Wesley, 1995.

<http://www.bell-labs.com/people/cope/Patterns/Process/section24.html>

[Cusumano95]

Michael A. Cusumano and Richard W. Selby, Microsoft Secrets, The Free Press, 1995. Reviewed

at <http://www.rational.com/connection/books/reviews/>

[Gabriel96]

Richard P. Gabriel, Patterns of Software, Oxford University Press, 1996.

[Gause89]

Donald C. Gause and Gerald M. Weinberg, Exploring Requirements, Dorset House, 1989.

Reviewed at <http://www.rational.com/connection/books/reviews/>

[Hohmann97]

Luke Hohmann, Journey of the Software Professional, Prentice Hall PTR, 1997.

[Kaner93]

C. Kaner, J. Falk, and H.Q. Nguyen, Testing Computer Software (2/e), Van Nostrand Reinhold,

1993. Reviewed at <http://www.rational.com/connection/books/reviews/>

[Kaner98]

Cem Kaner, course notes for Black Box Software Testing, Summer, 1998.

<http://www.kaner.com>. The discussion of bug analysis and reporting contains some excellent

material not present in [Kaner93].

[Lammers86]

Susan Lammers, Programmers at Work, Microsoft Press, 1986.

[Levy84]

Steven Levy, Hackers, Anchor/Doubleday, 1984.

[Maguire94]

Steve Maguire, Debugging the Development Process, Microsoft Press, 1994. Reviewed at

<http://www.rational.com/connection/books/reviews/>

[Marick95]

Brian Marick, The Craft of Software Testing, Prentice Hall, 1995.

[Marick97a]

Brian Marick, "Classic Testing Mistakes," Software Testing, Analysis, and Review (STAR)

conference, Software Quality Engineering, May 1997.

<http://www.stlabs.com/marick/classic.htm>

[Marick97b]

Brian Marick, "The Test Manager at the Project Status Meeting," International Quality Week

conference, Software Research, May 1997. <http://www.stlabs.com/marick/status.htm>

[Marick98a]

Brian Marick, "The Testing Team's Motto". <http://www.stlabs.com/marick/purpose-oftesting.

htm>

[Marick98b]

Brian Marick, "When Should a Test Be Automated?" International Quality Week conference,

Software Research, May 1998.

[Moore91]

Geoffrey Moore, Crossing the Chasm, HarperBusiness, 1991. Reviewed at

<http://www.rational.com/connection/books/reviews/>

[Norman90]

Donald A. Norman, The Design of Everyday Things, Doubleday, 1990.

[Nielsen93]

Jakob Nielsen, Usability Engineering, AP Professional, 1993.

[Pettichord98]

Bret Pettichord, "Developers Take On a Full System Test," Software Testing, Analysis, and

Review (STAR) conference, Software Quality Engineering, May 1998.

[Raymond96]

Eric S. Raymond (compiler), The New Hacker's Dictionary (3/e), MIT Press, 1996.

<http://www.logophilia.com/jargon/jargon.html>

[Turkel84]

Sherry Turkle, The Second Self: Computers and the Human Spirit, Simon & Schuster, 1984.

[Weinberg71]

Gerald M. Weinberg, The Psychology of Computer Programming, Van Nostrand Reinhold, 1971.

[Weinberg85]

Gerald M. Weinberg, The Secrets of Consulting, Dorset House, 1985.

[Weinberg93]

Gerald M. Weinberg, Quality Software Management, Volume 2: First-Order Measurement,

Dorset House, 1993.

[Weizenbaum76]

Joseph Weizenbaum, Computer Power and Human Reason, W.H. Freeman, 1976.

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值