《编程匠艺》读书笔记之六

第八章 测试时代

  • 不论你觉得自己是一位多么优秀的程序员,发布未经检测的软件都无异于自杀。
  • 有太多的软件工厂不是低估了详尽测试的重要性,就是试图把测试挤到发布软件之前的最后一分钟来匆忙的完成。
  • 测试是一种重要的代码构建技术。

    在软件开发中,以下几个术语是经常被混淆的:
  • “错误”:指某件你做错的事,它是一种特定的人类行为,会造成软件包含缺陷。
  • “缺陷”:指错误在软件中体现出来的后果。缺陷的隐蔽性使调试变得异乎寻常的困难。
  • “故障”:当缺陷被引发时,它可能会造成一个故障,但也可能不会。故障是缺陷的表现形式,也是我们真正担心的问题。它也许是我们唯一能注意到的事件。
  • “bug”:通常用作缺陷的同义词。
  • 良好的测试是一种技能,测试最重要的一条原则:就是要去做。
  • 测试是一种与调试截然不同并且相互独立的活动,尽管他们之间的界限很模糊,并且经常混淆在一起,测试是一种检验你的软件是否存在缺陷的系统过程,而调试则是一种跟踪造成缺陷的原因的过程。

    在软件的整个开发过程中,需要对各种不同的对象进行测试:
  • 大量的文档会经历一个测试阶段,通常也称为”审查“。
  • 实现规范的代码自然将在开发人员的计算机上进行测试。
  • 对最终产品进行测试,主要检查系统的功能是否和期望一致。
  • 测试和QA也是不一样的:测试的目标是找出错误的行为,即软件与它的规范不符的地方;QA是预防,它确保了我们的开发过程会生成高质量的软件。
  • 开发过程中每个人都会对制作高质量的软件有所影响,软件的质量并不是你可以在代码完成之后添加的品质。
  • 为什么要进行软件测试?1. 帮助我们找到缺陷并修正它们;2. 确保以后的版本中不会出现同样的缺陷。
  • 测试不能证明不存在缺陷,而只能证明存在缺陷。不要因为代码通过一组不完全的测试,就产生假的安全感。
  • 谁来进行测试?程序员有责任对他编写的源代码进行测试。
  • QA部门的工作是进行测试,但是应该测试产品,而不是测试新编写的代码。
  • 测试的内容有哪些?我们主要测试策略是演练所有代码,并通过编写更多的代码来验证它们的行为;另一种策略是直接检查代码,以验证它的正确性。
  • 何时进行测试?在编写代码的同时进行测试,这是找出代码错误的最早的机会,尽早的和详尽的进行测试,是确保软件质量最有效的方式。
  • bug的成本是会随着开发过程的进行而日益增加,所以尽可能早一些开始测试代码非常重要。
  • 测试驱动开发主张将测试作为一种核心的构造手段,即你在编写被测试的代码之前就开始编写测试它的代码。
  • 这种策略有深刻的寓意:当你开始考虑编写某段代码时,你必须同时考虑如何对它进行测试,这将使你设计代码的方式更臻完善。
  • 持续集成的策略:我们执行测试越频繁,就越有可能发现问题。

    为了测试一段特定的代码是否可以正常运行,你需要一个测试用例来验证以下两点:
  • 对于所有有效的输入,都会产生正确的输出。
  • 对于所有无效的输入,都会产生适当的故障行为。
  • 一般人都很容易相信自己所读到的代码,并相信它是正确的,在你刚写下一些代码时,只会读到你想写的内容,而不是你真正写下的东西,要学着看两遍,用批评的眼光来阅读所有的代码。

    导致测试比较难以进行的因素包括:
  • 代码规模。
  • 依赖关系。
  • 外部输入。
  • 外部激励。代码可以对除函数调用之外的激励作出反应。如果这些激励可能出现的时间和次数都不确定,那么就会特别麻烦。
  • 线程。多个控制线程会使测试更加复杂。
  • 演变。
  • 硬件缺陷。
  • 难缠的故障形式。代码可能会以多种令人惊讶和奇怪的方式崩溃。
  • 编写测试用例绝非易事,当组件组合在一起并开始相互依赖时,软件的复杂度将城指数级增长,所有这些问题凑在一起,会让你的生活变得复杂无比。
  • 无论你多努力的进行测试,仍然不能制作出无缺陷的软件,现实世界中的测试极少能证明软件刀枪不入,而仅仅能证明它是可以用的而已。

    软件测试类型可以分为:
  • 单元测试。通常用来表示测试代码的一个模块,它实际上描述的是对原子单元的测试。
  • 组件测试。用于验证由一个或多个单元组成的完整组件的行为。
  • 集成测试。对放入一个系统的组件的组合进行测试,确保它们之间正确的相连。
  • 回归测试。对软件或其环境进行修正或休整之后重新进行的测试。
  • 负载测试。确保代码可以处理抛给它的预期的数据量,负载测试可检查程序是否如预期的是”向上扩展的“。
  • 压力测试。在很短的时间内向代码抛出大量的数据,以观察代码会出现什么状况。其目的是确保代码在受到真正冲击时不会乱作一团。这种测试通常用在多线程或实时系统中。
  • 疲劳测试。与压力测试相似,它关注代码在较高的负载下运行一段很长的时间,以确定任何会在执行大量的操作之后会出现的性能问题。它可以发现一些其他测试无法检测到的缺陷。
  • 可用性测试。通常在可控度很到的可用性实验室中进行。

    我们在进行你给软件测试设计时,主要有以下两种思路:
  • 黑盒测试。也称功能测试。
  • 白盒测试。也称结构测试。其工作量非常大,并且成本要远远高于黑盒测试。
  • 黑盒测试关心的是代码缺少什么,白盒测试关心的是代码在功能上的缺陷。
  • 编写一个全面的测试用例,其中每个测试针对代码的一个方面,15个重复指出相同缺陷的测试,远不如15个分别指出15个不同缺陷的测试有用。
  • 你必须选择那些最有可能找出软件缺陷的测试,而不是执行那些只是重复的指出很少几个相同问题的测试。与目的不符的测试,可能只会把错误的事情变得更糟。

    在进行黑盒测试时,可以使用以下一些测试用例:
  • 一些良好的输入。
  • 一些不好的输入。
  • 边界值。
  • 随机数据。
  • 零。
  • 你所编写的单元测试的质量,在很大程度上取决于你要测试的代码接口的质量。
  • 编写清晰的API,减少对其他代码的依赖,以及断开对其他组件的任何写死的链接。
  • 为测试而设计,就会以一种合理的、可理解的和可维护的方式来构造代码,你将减少组件的耦合度并增强其聚合度。

    应用以下原则,有助于编写具有非常好的可测试的代码:
  • 使各个代码部分都自包含,而不要与外部世界建立未经说明的或缺乏实质的依赖关系。
  • 不要依赖全局变量,将这种状态集中到一个作为自变量传递的共享结构中。
  • 限制你的代码的复杂度,把代码分为较小的、可以理解的并且可以单独进行测试的代码块。
  • 保持代码的可观测性。
  • 尽可能让你的代码测试自动的进行,这要比手动测试更快、更容易、更安全,而且可以使测试更有规律的进行。
  • 将自动的进行单元测试作为你的构建过程的一部分。

    当我们遇到一个程序故障时,可以按照以下步骤进行处理:
  • 注意当时你做了什么,以及是什么操作引发了这个故障。
  • 再试一次,查明这个问题是否是可重复的,出现的频率如何,以及是否与同事进行你给的任何其他活动相关。
  • 完整的描述故障,要非常具体。
  • 将这些描述记录下来。
  • 编写可以检测出这个缺陷的最简单的测试用例。
  • 难以解决的故障是不规则的,甚至是随机的,从而很难说明它们的特征。
  • 你必须运用系统化的方法来查找缺陷,你还必须运用系统化的方法来管理和处理缺陷。

    缺陷跟踪系统是我们在管理缺陷是使用的一个重要武器,这个工具是一个专用的数据库,它的接口对每个参与测试过程的人都可见。通常的操作包括:
  • 报告故障。
  • 分配责任。
  • 确定报告的优先级。
  • 标记为已修正。
  • 结束报告。
  • 查询数据库。
  • 修改条目。
  • 任何程序员编写测试都有一个秘密的目标:证明他的代码是正确的,而不是找出它有问题的地方。
  • 优秀的程序员:1. 为他们所写的代码编写测试;2. 在微观层次上进行测试,以便宏观层次的测试不会被愚蠢的编码错误妨碍;3. 关心产品质量并为之负责,在整个测试工作中发挥他们的作用。
  • 糟糕的程序员:1. 不认为测试是软件开发的一个重要和必要的部分,觉得这是别人的工作;2. 将未经测试的代码提交给QA部分,当测试发现有缺陷的行为时感到很意外;3. 由于问题发现的很晚,所以让自己生活变得更加复杂——不及早做充分的测试,就会被许多难以定位iede缺陷击倒。
posted @ 2008-10-26 17:41  李潘  阅读(405)  评论(0编辑  收藏  举报