不同类型的测试
测试是一个极其广泛的领域。从表面上看似乎很简单,但实际上并非如此。例如,我们可以将测试定义为检查软件是否符合其预期用途。但它涵盖的内容远不止于此:例如,变异测试就是验证断言是否真的有效。在本文中,我想探讨几种不同的测试方法,它们各自的用途以及彼此之间的区别。
测试的必要性
理想情况下,我们不需要测试。我们只需编写无bug、高性能的代码,就能完美地直接实现需求。
然而,将需求转化为代码的过程需要进行检验。软件开发涉及需求收集、编写文档、与人沟通、处理组织问题等等。为了简化讨论,本文余下部分将假设这个过程是完美的——尽管它并非如此——并将重点放在代码本身。
我们不妨直面这个显而易见的问题:一些平台,例如 Coq,确实支持形式化证明。这意味着可以从数学角度证明代码没有 bug。然而,大多数此类平台都局限于学术界。据我所知,在工业界,形式化验证的应用并不多见。
如果从等式中移除形式化证明(此处双关),那么剩下的唯一选择就是测试。但测试并不能证明没有缺陷。事实上,测试不仅仅关乎缺陷:它涵盖了许多不同的领域。让我们来看看其中的一些领域。
单元测试
单元测试是一门相当成熟的学科:无论使用何种编程语言,都已有大量书籍出版。它们的内容通常大同小异。
[...] 单元测试是一种软件测试方法,通过这种方法测试源代码的各个单元 [...] 以确定它们是否适合使用。
唯一存在激烈争论的点是单元的定义:在面向对象编程中,有人认为单元是类;另一些人则认为单元是模块 ,即一组相互协作的类。
集成测试
单元测试的定义和理解都相当完善,相比之下,集成测试似乎还是一片空白领域。事实上,我之所以写《实战中的集成测试》这本书,正是因为当时我找不到其他关于这个主题的书籍。
集成测试 [...] 是软件测试中的一个阶段,在这个阶段中,各个软件模块被组合在一起,作为一个整体进行测试。
集成测试的核心概念之一是被测系统(SUT)。在单元测试中,SUT指的是单元(即上文所述的类或模块)。而在集成测试中,每个测试都需要定义 SUT:它可以小到两个单元协同工作,也可以大到整个系统。
关于单元测试和集成测试孰优孰劣的争论一直很激烈。有些人只考虑前者,而有些人只考虑后者。如果你想了解我个人对此的看法,请阅读我之前的文章。
端到端测试
在大多数应用程序仍采用人机交互的时代,端到端测试意味着测试从用户界面到数据库再返回的整个流程。由于 Web 应用程序几乎无处不在,这涉及到浏览器。因此,端到端测试与之前的方法截然不同。即使可用工具随着时间的推移而不断改进,自动化用户与浏览器的交互仍然不是一件容易的事。
端到端测试中最大的问题在于用户界面层的脆弱性。现代架构通常会将前端(基于 JavaScript)和后端(基于REST)清晰地分离出来。为了应对这种脆弱性,最好先通过集成测试来测试 REST 层。
端到端测试包含从单元测试到集成测试的完整过程。著名的测试金字塔很好地概括了这一点。
可以将端到端测试视为集成测试最完整的形式:在这种情况下,被测系统就是整个应用程序。
系统测试
随着时间的推移,越来越多的应用面向机器对机器(M2M)交互,而非人机(H2M)交互。端到端测试之于人机交互,正如系统测试之于机器对机器交互。
这样做的好处是无需浏览器参与。因此,单元测试和集成测试中常用的自动化技术可以复用。
性能测试
以往的大多数方法都侧重于测试功能需求。人们很容易忘记,软件组件的适用性不仅包括功能需求,还包括非功能需求。性能、可靠性等都属于非功能需求的范畴。
请注意,性能测试中的被测系统 (SUT)可以是整个系统,也可以只是某个被认为至关重要的子组件。然而,在这种情况下,仅仅断言每个单独组件的性能可能不足以确保整个系统的性能。
[...] 性能测试通常是一种测试实践,用于确定系统在特定工作负载下的响应能力和稳定性。
根据我的经验,性能测试这个词被过度使用,而且过于笼统。它包含很多方面:以下是最常见的几个方面。
负载测试
人们谈论性能测试时,通常隐含的就是负载测试。在负载测试中,需要设置不同的测试参数,这些参数模拟生产环境中的典型负载。但实际上,大多数组织无法提供高质量的生产负载样本,因此负载只能通过推断得出。
例如,在电子商务环境中,测试参数可能包括:
- 不同的请求类型:浏览目录、查看特定产品、将产品添加到购物车、结账、付款等。
- 例如,浏览占比70%,查看占比20%,购物车操作占比6%,结账占比3%,支付占比1%。
- 并发请求数
- 并发用户会话的数量,以及它们在请求之间的分布情况
- ETC。
耐久性测试
在常规负载测试中,测试会一直运行直到执行完毕。其目的是检验系统在特定负载下是否按照预期运行。
耐久性测试(也称浸泡测试)的目标是检验系统性能随时间推移的变化情况。相比之下,这类测试的持续时间通常以天甚至周为单位。
浸泡测试是指在连续可用期间,使用典型的生产负载对系统进行测试,以验证系统在生产使用下的性能。
压力测试
压力测试是指对系统进行测试,直至其崩溃。测试过程中,负载会逐步增加。压力测试的目标是了解系统的性能极限。
压力测试(有时也称为极限测试)是一种故意施加极大强度或彻底性的测试,用于确定特定系统、关键基础设施或实体的稳定性。它涉及超出正常运行能力的测试,通常会达到极限,以观察测试结果。
此类实验的结论大致如下:在x每秒请求数以百万计的负载中,一部分y请求会返回5xx错误代码。
混沌测试
混沌测试,也称为混沌工程,是一种随机移除系统组件以了解其故障方式并检查其在压力下的整体恢复能力的做法。
混沌工程是一门在生产环境中对软件系统进行实验的学科,目的是建立对系统承受动荡和意外情况能力的信心。
混沌测试的本质特征在于它通常在生产环境中进行。但这并不意味着在其他环境中进行的实验就不能提供可操作的反馈,恰恰相反。然而,每个人都可能遇到过在生产环境中从未出现过的问题。根本原因可能是架构略有不同、数据不同、负载更高等等:归根结底,唯一需要经过实战检验且万无一失的环境就是生产环境。
虽然乍一看似乎有悖常理,但这种想法现在已经在那些认真对待系统韧性的成熟组织中根深蒂固。
安全测试
就像性能测试这个术语过于宽泛一样,安全测试也涵盖了许多不同的领域。最广为人知的安全测试形式是渗透测试。
渗透测试 [...] 是对计算机系统进行的经授权的模拟网络攻击,目的是评估系统的安全性。
[...]
该测试旨在识别系统的弱点 [...] (包括未经授权方访问系统功能和数据的可能性)以及优势,从而完成全面的风险评估。
基因突变检测
大多数测试方法都侧重于软件组件或整个系统的适用性。变异测试的独特之处在于,它的目标是对单元测试进行反思,更具体地说,是对那些具有误导性的代码覆盖率指标进行反思。
变异测试 [...] 用于设计新的软件测试并评估现有软件测试的质量。
我之前已经写过关于这个主题的文章。如果您想了解更多,请阅读那篇文章。
探索性测试
许多测试方法都侧重于回归错误,即在之前运行正常的软件中出现的错误。为此,测试都是自动化的:每次构建都会运行测试。如果之前运行的测试失败,则构建也会失败。
探索性测试的目标是发现测试框架无法检测到的缺陷。为了实现这一目标,需要以全新且出乎意料的方式与系统进行交互。
探索性测试是一种软件测试方法,它简明扼要地描述为同时进行学习、测试设计和测试执行。
由于其探索性本质,这个过程需要人工操作。我的经验告诉我,探索性测试的价值被低估了。这很可惜,因为我亲眼目睹过一些测试人员发现了系统中之前自动化测试步骤未能发现的问题。
尽管探索性测试本质上是手动操作,但它并非随意点击。它是一种工程实践,需要严谨的纪律和细致的方法来记录导致问题的步骤。优秀的测试人员还能够:
- 为了找出问题所在
- 为了重现导致这一结果的步骤
- 为了以一种能够减轻需要修复该漏洞的开发人员工作量的方式来创建这个问题
用户验收测试
大多数测试都由技术人员执行,例如开发人员、自动化测试人员等。相反,验收测试则由非技术人员执行,例如业务分析师或最终用户。顾名思义,验收测试的目标是评估他们是否接受该软件并认为其符合他们的使用需求。
用户验收测试是指验证解决方案是否对用户有效的过程。
概括
| 种类 | 目标 | 演员 | 自动化 |
|---|---|---|---|
| 单元 | 评估代码单元的适用性 | 开发者 | ✅ |
| 一体化 | 检查各单位的协作情况 | 开发者 | ✅ |
| 系统 | 验证系统的整体行为 | 具备不同技能组合:后端开发人员、系统管理员、数据库管理员等。 | ❌ |
| 端到端 | 验证从 GUI 到数据存储以及返回的流程。 | 前端开发工程师;专业自动化测试工程师 | ✅ |
| 表现 | 检查不同的性能相关指标 | 具备专业测试技能的系统管理员 | ✅ ❌ |
| 混乱 | 验证系统的弹性 | 具备不同技能的人员组合:系统管理员、网络专家等。 | ✅ |
| 渗透 | 评估系统的脆弱性 | “黑客” | ❌ |
| 突变 | 检查测试质量 | 开发者 | ✅ |
| 探索性的 | 找出隐藏的漏洞 | 手动测试员 | ❌ |
| 用户验收测试 (UAT) | 验证系统是否符合规范 | 商界人士 | ❌ |
结论
这篇文章已经太长了,而我只是浅尝辄止地介绍了几种可用的测试方法。总之,有几点需要记住:
- 找到系统中的漏洞比指出其根本原因要容易得多。被测系统越大,难度就越大。
- 需要找到手动测试和自动化测试之间的适当平衡:这种平衡取决于具体情况。
- 测试的核心在于投资回报率。这其实就是风险管理:目标是评估风险,包括风险的性质、概率、影响、相应的缓解措施等等,并考虑相关成本。
- 最后,测试是一项战略举措:在编写任何测试用例之前,应该仔细计划测试工作及其背后的原因(是的,这与TDD相悖)。
测试愉快!
更进一步:
- 为什么要测试你的软件?
- 从 Cucumber 开始进行端到端测试
- 单元测试与集成测试,为何存在对立关系?
- 软件性能测试
- 单元测试
- 集成测试
- 软件测试
- 浸泡测试
- 压力测试
- 混沌工程
- 安全测试
- 渗透测试
- 基因突变检测
- 探索性测试
- 验收测试
- 烟雾测试
本文最初发表于A Java Geek 网站,日期为2020 年8 月 2日。
文章来源:https://dev.to/nfrankel/ Different-kinds-of-testing-j9f
