等价划分:通过实例解释测试设计技术

  1. 首页
  2. 博客
  3. 软件测试

软件测试

等价划分:通过实例解释测试设计技术

您无法测试所有可能的输入,那么您应该选择哪些值?等价划分给你一个明确的答案。逐步学习该方法,通过实际示例进行操作,看看哪里需要帮助。

阅读约 8 分钟
通过实例解释等价划分测试设计技术

要点速览

  • 等价分区将输入分为应具有相同行为方式的组。
  • 您测试每组中的一个代表值,而不是每个值。
  • 始终包括无效分区,因为错误处理是许多错误隐藏的地方。
  • 它与边界值分析一起使用效果最好,因为错误通常位于组的边缘。

一旦计算负数、大量数字、文本和空白,接受 18 到 60 岁的年龄字段就有大量可能的输入。没有人能够全部尝试。 等价分区解决了这个问题,方法是对软件应以相同方式处理的输入进行分组,然后测试每组中的一个值。

这是每个测试人员应该首先学习的测试设计技术之一。在本指南中,我们将逐步完成它,并用 Python 测试一个示例。我们在 Python 3.13 上运行该代码,并包含其输出。下面的密码和优惠券规则是说明性示例,并非取自真实系统。

什么是等价划分?

等价划分,也称为等价类划分(ECP),是一种黑盒测试设计技术。它将特征的可能输入划分为分区或类。分区内的每个值都应该产生相同类型的行为。

因为您不需要查看代码,所以可以直接从需求中应用它。要了解有关这种测试风格的更多信息,请阅读黑盒与白盒测试。

为什么它有效

该技术基于一个假设:如果分区中的一个值发现错误,则该分区中的其他值也可能会发现它。如果一个值通过,其他值也可能会通过。在成人年龄领域中测试 25、26 和 27 岁可能不会告诉您任何单独测试 25 岁所无法得出的信息。

方法 年龄字段测试 结果
详尽无遗 每个可能的值 实践中不可能
随机猜测 无论想到什么 重复和空白
等价划分 每个分区一个 小,有意覆盖

但这只是一个假设,而不是保证。软件可以将特殊情况隐藏在分区内,这是我们稍后会讨论的限制。

如何通过五个步骤完成

  1. 阅读需求并识别输入及其规则。
  2. 找到有效分区,它们是应该接受的值组,并注意不同范围的行为是否不同。
  3. 查找无效分区,包括值太低、太高、类型错误和空白。
  4. 从每个分区中选择一个代表值,通常在中间的某个位置。
  5. 写下每个结果的预期结果,就像任何好的测试用例一样。

工作示例:年龄字段

要求: 18岁至60岁的用户均可注册。

分区 类型 测试值 预期结果
18岁以下 无效 10 被拒绝
18 至 60 有效 35 已接受
60岁以上 无效 75 被拒绝
不是一个数字 无效 “abc” 错误
空白 无效 (empty) “必填”消息

五个测试可以覆盖需求中的每个行为。许多教程停在前三行,但最后两行是真实用户偶然触发的。您还可以添加负数或小数行,如果可以合理地以不同方式处理这些行。

在代码中测试它

下面是该规则作为一个函数,每个分区有一个单元测试,使用 Python 的内置 unittest:

import unittest

def can_register(age):
    if not isinstance(age, int) or isinstance(age, bool):
        raise TypeError("age must be a whole number")
    return 18 <= age <= 60

class EquivalenceTests(unittest.TestCase):
    def test_valid_partition(self):
        self.assertTrue(can_register(35))

    def test_too_young_partition(self):
        self.assertFalse(can_register(10))

    def test_too_old_partition(self):
        self.assertFalse(can_register(75))

    def test_wrong_type_partition(self):
        with self.assertRaises(TypeError):
            can_register("abc")

我们的运行通过了:

test_too_old_partition ... ok
test_too_young_partition ... ok
test_valid_partition ... ok
test_wrong_type_partition ... ok

在实际项目中,pytest 的参数化功能 是通过单个测试运行分区值表的一种巧妙方法。

工作示例:密码

要求: 密码长度必须为 8 到 20 个字符,并且至少包含一位数字。

这个有两个规则,长度和内容,所以我们考虑对这两个规则进行分区:

分区 类型 测试值 预期结果
有效长度,有数字 有效 日出42 已接受
太短 无效 ABC1 错误:太短
太长 无效 带数字的 30 个字符值 错误:太长
有效长度,无数字 无效 日出日 错误:需要数字
空白 无效 (empty) 错误:必填

请注意,每个无效测试仅破坏一条规则。如果一个值太短并且缺少一个数字,则通过的测试无法告诉您哪项检查有效。一张支票可能被破坏,而另一张支票则掩盖了事实。

工作示例:非数字输入

该技术不限于数字范围。离散选项、类别和状态也形成分区。假设一家商店有这样的规则:

要求: 金卡会员免费送货,银卡会员免费送货 50 次,其他会员免费送货 100 次。未知的层值是错误。

分区 测试值 预计费用
黄金 黄金 0
银牌 银 50
无会员资格 (none) 100
未知等级 铂金 错误

在这里,每个选项都是其自己的分区,因为每个选项的行为都应该不同。其他常见分区包括文件类型(例如有效和无效上传)以及基于输出的组(例如批准或拒绝的贷款)。

提示: 考虑输出和输入。如果计算可以产生折扣、无折扣或警告,请确保至少有一个测试达到这些结果中的每一个。

一次多个输入

有了多个字段,组合的数量就会快速增长。三个字段有四个分区,每个字段提供 64 种组合。两种策略有帮助:

  • 弱覆盖: 确保每个分区至少出现在一次测试中,并在每个测试中组合多个有效 值。对于无效值,请一次测试一个,以便错误不会相互掩盖。
  • 基于规则的覆盖:当组合实际改变结果时,例如会员状态与订单值,使用决策表测试正确列出它们。

与边界值分析配对

分区告诉您要测试哪些组。它几乎没有提到一群虫子喜欢隐藏在哪里,而且许多虫子都藏在边缘。为了证明这一点,我们编写了一个有错误的版本,其中包含经典的差一错误:

def buggy(age):
    return 18 < age <= 60     # should be 18 <= age

[buggy(a) for a in (35, 10, 75)]   # [True, False, False]
buggy(18)                          # False

我们的分区值 35、10 和 75 都给出了正确的答案,因此这些测试将通过。然而,一名 18 岁的年轻人却被错误地拒绝了。这就是为什么这两种技术属于一起的原因:

技术 选择 18至60岁的价值观
等价划分 每组内有一个值 10, 35, 75
边界值分析 每组边缘的值 17, 18, 60, 61

使用分区来查找组,然后使用边界来测试组相遇的位置。在我们的代码中,对 17、18、60 和 61 的单独测试传递了正确的函数,并且会捕获该错误。我们的边界值分析指南完整地涵盖了这一点。

优点和局限性

优点 局限性
大大减少了测试次数 依赖于分区是否正确
提供清晰、可解释的覆盖范围 可能会错过边界处的错误
无需访问代码 可能会错过隐藏的特殊情况
尽早申请,甚至符合要求 不能很好地建模序列或组合

最大的风险是错误的假设。如果代码对某个特定值有隐藏规则,您选择的测试可能根本达不到它。 探索性测试和代码覆盖率可帮助您找到分区遗漏的内容。

常见错误

  • 忘记无效分区。仅测试快乐路径,不会检查错误处理。
  • 测试一个分区中的多个值。这会在没有新信息的情况下增加成本。
  • 在一个测试中组合多个无效值。一个错误可能隐藏另一个错误。
  • 跳过边界。单独的分区测试经常会错过差一的错误。
  • 猜测规则。如果需求不清楚,请询问。 60岁应该包括60岁半吗?

最后的话

等价分区取代了“我应该编写多少个测试?”一个更好的问题:“存在哪些行为,我是否涵盖了每一种行为?”要尝试一下,请拿一个您今天正在测试的表格,并在纸上列出其分区,包括无效的分区。接下来添加边界。您通常会发现更少的测试却发现更多的错误。

常见问题

什么是测试中的等价划分?

它是一种黑盒技术,可将输入分为预期表现相似的组。然后,您测试每组中的一个代表性值,而不是每个值。

等价划分和边界值分析有什么区别?

分区从每一组中选择一个值。边界值分析测试这些组边缘的值。它们通常一起使用。

等价划分需要多少个测试用例?

通常每个分区至少有一个,有效的和无效的。一个年龄范围字段通常需要大约三到五次测试。

等价划分是黑盒还是白盒?

这是一种黑盒技术,因为分区来自需求并且不需要代码。然而,开发人员也可以将其应用于单元测试。

什么是有效分区和无效分区?

有效分区包含系统应该接受的值。无效分区包含应拒绝的值,例如超出范围的数字、错误的类型和空格。

继续学习

与Stack Quora合作

需要测试、WordPress 或 SEO 方面的帮助?

我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。

继续阅读

更多指南 测试、人工智能和现代开发

喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。

人工智能

人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它

人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。

阅读文章 →
DevOps

环境变量解释:.env 文件、秘密和配置正确完成

使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。

阅读文章 →
软件开发

API 速率限制解释:算法、标头和处理 429 错误

API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。

阅读文章 →
测试自动化

Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误

使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。

阅读文章 →
软件测试

代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义

用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。

阅读文章 →
数据库与 SQL

SQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。

阅读文章 →
数据库与 SQL

SQL 视图解释:创建、使用和避免陷阱(附示例)

SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。

阅读文章 →
软件开发

TypeScript 接口与类型:真正的差异,使用编译器进行测试

TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。

阅读文章 →
软件开发

Python *args 和 **kwargs 解释:灵活的函数和清晰的示例

Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。

阅读文章 →
软件开发

Python Lambda 函数:语法、实际用途以及何时不使用它们

Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。

阅读文章 →
人工智能

什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环

什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。

阅读文章 →
DevOps

监控与可观察性:真正的区别,包括日志、指标和跟踪

监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。

阅读文章 →
DevOps

Docker Volumes 与 Bind Mounts:以正确的方式保留容器数据

Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。

阅读文章 →
测试自动化

TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式

TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。

阅读文章 →
软件测试

用户验收测试 (UAT):流程、清单和真实示例

用户验收测试解释:谁运行它、它与 QA 有何不同、七步流程、UAT 测试用例模板、清单、签核标准和常见错误。

阅读文章 →
数据库与 SQL

SQL NULL 值解释:IS NULL、COALESCE、NULLIF 和要避免的陷阱

通过我们运行的查询解释了 SQL NULL 值:三值逻辑、IS NULL、COALESCE、NULLIF,聚合、连接和排序如何处理 NULL,以及它悄悄引起的错误。

阅读文章 →
数据库与 SQL

SQL CASE WHEN 语句:语法、实例和常见错误

SQL CASE WHEN 解释了我们运行的查询:简单和搜索表单、条件顺序、SUM 透视、COUNT 技巧、自定义排序、更新和要避免的 NULL 陷阱。

阅读文章 →
软件开发

什么是智威汤逊?通过工作示例解释 JSON Web 令牌

什么是智威汤逊? JSON Web Tokens 解释:这三个部分,在我们运行的代码中构建和篡改的令牌,JWT 与会话,存储,刷新令牌和安全错误。

阅读文章 →
软件开发

JavaScript 事件循环解释:调用堆栈、微任务和计时器以及示例

JavaScript 事件循环用我们运行的代码进行了解释:调用堆栈、任务和微任务队列、为什么 setTimeout 0 不是立即执行、阻塞、饥饿和面试测验。

阅读文章 →
软件开发

Python 生成器和 Yield 解释:通过真实示例节省内存

Python 生成器和yield 用我们运行的代码进行了解释:它们如何暂停和恢复、测量内存节省、管道、无限序列、yield from、发送和常见陷阱。

阅读文章 →
人工智能

什么是嵌入?向量和余弦相似度简单解释

什么是嵌入?关于向量、余弦相似度和语义搜索的简单语言指南,其中包含我们运行的一个小示例、常见用途、选择模型和已知限制。

阅读文章 →
DevOps

蓝绿部署与金丝雀部署:差异、权衡以及如何选择

蓝绿部署与金丝雀部署解释:每种部署的工作原理、成本、风险和回滚、我们测试的稳定用户分桶、数据库迁移、滚动更新以及如何选择。

阅读文章 →
测试自动化

不稳定的测试:造成这些问题的原因以及如何彻底解决它们

我们运行的演示解释了不稳定的测试:主要原因、如何确认和诊断、正确的时间修复、共享状态和日期以及隔离过程。

阅读文章 →
软件测试

负载测试与压力测试:差异、指标和 k6 示例

负载测试与压力测试的解释:各自证明什么、峰值和浸泡测试、重要的指标、为什么平均值会误导、k6 个示例以及如何计划测试。

阅读文章 →
软件测试

测试用例与测试场景:主要区别、示例以及何时使用它们

测试用例与测试场景的解释:清晰的定义、并排的表格、从场景到案例的密码重置功能、模板以及何时使用每个功能。

阅读文章 →
数据库与 SQL

SQL 查询优化:读取执行计划并修复慢速查询(带计时)

对一百万行进行 SQL 查询优化测试:读取执行计划、添加正确的索引、修复不可控制的过滤器、使用复合索引和键集分页。

阅读文章 →
数据库与 SQL

SQL UNION 与 UNION ALL:差异、示例和性能测试

SQL UNION 与 UNION ALL 通过我们运行的查询进行了解释:重复、都遵循的规则、真实的数据错误、测量的性能、INTERSECT 和 EXCEPT 以及何时使用它们。

阅读文章 →
软件开发

JavaScript Fetch API 教程:GET、POST、错误处理和超时

针对本地服务器测试的 JavaScript Fetch API 教程:GET 和 POST、为什么不抛出 404、可重用的帮助程序、查询字符串、超时、CORS 和常见错误。

阅读文章 →
软件开发

Python 文件处理:读取、写入、CSV、JSON 和 pathlib 以及示例

使用我们运行的代码解释了 Python 文件处理:打开模式、使用、读取大文件、CSV、JSON、pathlib、二进制文件、安全写入、错误处理和常见错误。

阅读文章 →
软件开发

Python 类和对象:带有实际示例的 OOP 教程

用我们运行的代码解释 Python 类和对象:__init__、方法、类与实例属性、继承、属性、dunder 方法、数据类和常见错误。

阅读文章 →
人工智能

什么是大语言模型?法学硕士如何运作,在没有炒作的情况下进行解释

什么是大语言模型?法学硕士用简单的语言解释:标记、嵌入、注意力、训练、温度和上下文窗口、一个微小的下一个单词演示和真正的限制。

阅读文章 →
DevOps

适合初学者的 Linux 命令:带有示例的基本终端指南

适合初学者的 Linux 命令,按任务分组:导航、文件、查看日志、grep、管道、权限、进程和档案,并附有实用的日志分析示例。

阅读文章 →
测试自动化

Selenium 与 Playwright:主要区别、代码比较以及选择

Selenium 与 Playwright 比较:架构、等待、浏览器和语言支持、调试、两种工具中的相同测试、迁移建议以及如何选择。

阅读文章 →
软件测试

等价划分:通过实例解释测试设计技术

等效分区解释:如何找到有效和无效分区,年龄、密码和优惠券的工作示例,我们运行的测试,以及为什么将其与边界配对。

阅读文章 →
软件测试

软件测试中的严重性与优先级:差异、矩阵和实例

软件测试中的严重性与优先级解释:清晰的定义、带有示例的四向矩阵、级别尺度、谁设置每个级别以及如何运行错误分类。

阅读文章 →
数据库与 SQL

SQL 子查询教程:类型、相关子查询和 EXISTS 以及示例

SQL 子查询教程,其中包含我们运行的查询:标量、IN、FROM 和 SELECT 子查询、相关子查询、EXISTS、NOT IN NULL 陷阱以及子查询与 JOIN 与 CTE。

阅读文章 →
数据库与 SQL

SQL 事务和 ACID 解释:COMMIT、ROLLBACK 和隔离级别

SQL 事务和 ACID 通过我们运行的银行转账进行了解释:COMMIT、ROLLBACK、保存点、锁、隔离级别、脏读、死锁和最佳实践。

阅读文章 →
软件开发

Git merge 与 rebase 的区别:示例及如何选择

用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。

阅读文章 →
软件开发

Python 列表推导式:语法、示例与何时避免使用

Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。

阅读文章 →
数据库与 SQL

SQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)

通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。

阅读文章 →