Logo
Section 1 / 3

软件测试入门

了解软件测试是什么,以及如何在 Ruby 语言中进行测试。

导入

软件测试入门

了解软件测试是什么,以及如何在 Ruby 语言中进行测试。

什么是软件测试?

软件测试的定义非常广泛,因为存在多种测试方法。总体而言,测试的目的是确保软件能够按预期工作。

想象一位硬件工程师刚刚用一组元器件组装了一台收音机,她需要判断这台设备是否正常工作。她可能会进行所谓的“冒烟测试”——快速开关电源,观察是否有冒烟现象,以发现是否存在根本性故障。如果没有冒烟,就可以进入“正常流程”测试:打开收音机,尝试调频到某个电台,检查是否有声音输出。

Course Image

在收音机投入生产之前,还可能进行其他类型的测试,例如负载测试,用于测量功耗。收音机应具备良好的能效,这样用户才不会每天更换电池。即使收音机能正常播放声音,但如果电池消耗过快,仍可能成为市场失败的原因,导致产品无法成功。

测试类型

在软件领域,存在众多测试方法,如手动测试、自动化测试、单元测试、集成测试、负载测试等。要全面掌握这些概念需要大量时间。每种方法都有成百上千本书籍专门介绍,因此我们不妨聚焦于软件开发者日常工作中最常遇到的测试类型:单元测试。那么,什么是单元测试?我们为什么需要它?

大约30年前,几乎没有人将软件测试视为重要环节。程序通常在文本编辑器中编写,开发人员直接运行程序(或通过软盘、光盘,后来通过互联网发送给客户),一旦出现错误或行为异常,再进行修复并发布新版本。这些版本通常包含一系列缺陷修复。

然而,随着软件产品的复杂度不断提升,团队中的开发人员数量也在增加。一个微小的改动就可能引入新的缺陷。尽管部分缺陷可以通过人工测试(或由测试人员)发现,但这种方式已远远不够。

于是,一个问题浮现出来:能否在早期阶段就识别出潜在的缺陷?对于一个软件模块或关键组件,是否能让它更加稳健、更难出错?这就像现实生活中的早晨例行检查:出门前我们会反复确认电熨斗和炉灶是否关闭。虽然我们几乎可以肯定它们已经关掉,但验证的成本几乎为零,而一旦疏忽带来的损失却可能是巨大的。

编程也是如此:

  • 我们不再检查电熨斗或燃气灶,而是对程序中的各个单元进行双重确认。
  • 不再只做一次检查,而是每次代码变更后都运行自动化的检测。

我们是否认同这样做更高效?软件开发者修改代码后立即运行测试,确保一切按预期运行。如果出现问题,可以立刻修复,而无需将有缺陷的软件交付给用户或上线发布。因此,发现缺陷的时间从几天缩短到几秒甚至几分钟(例如运行500个自动化测试通常只需几分钟)。

对于一个相对简单的项目,每100次变更可能需要运行500次测试,这意味着总共约有5万次测试执行。可以想象,要在已有项目中引入测试是多么困难,尽管这会显著提升软件质量。然而,单元测试并非免费的。

工程师必须编写这些测试。虽然编写测试并不复杂,但需要投入时间和精力,企业必须为此投入人力和资金。高质量的测试需要掌握测试框架、测试方法论,并积累一定的实践经验。

同时,不可能通过测试覆盖所有情况。假设程序中有10个 if...else 语句,每个语句有两种可能的分支路径,那么总的代码执行路径组合数为 2¹⁰ = 1,024 种。即便只是少量条件,也需要多达上千次测试。

有些项目会使用“代码覆盖率”这一指标,以百分比表示。我们常听到“我们的项目代码覆盖率达到了80%”,这通常被视为值得骄傲的成绩。但问题是,这个数字是如何计算出来的?确实,某些模块可能被测试覆盖得非常充分。然而,即使达到100%的覆盖率,真实程序的实际执行路径数量也远超测试用例的数量,尤其是当程序超过千行代码时。

此外,开发人员往往在编写代码的同时就创建测试。但软件设计在初期阶段通常尚未定型。程序员像艺术家一样,会在最终作品成型前绘制多幅草图进行尝试。第一次就完美定稿几乎是不可能的。而大量的单元测试恰恰能在设计尚处于初期阶段时,帮助固化合理的架构。

当我们删除冗余代码时,也必须删除相关的测试。从商业角度看,这相当于在花双倍的钱去处理本已投入成本的事物。

尽管存在上述挑战,单元测试无疑是一种能显著提升软件质量的实践。企业深刻认识到其价值,大力投资构建可靠的软件解决方案。如今,单元测试已成为标准做法,几乎所有现代软件框架都内置了测试工具支持。

Ruby 版本管理器

RSpec 测试框架