测试用例设计方法实战 – 等价类划分与边界值分析的正确打开方式

前言

做测试这些年,我见过太多同行写用例的方式:要么照着需求文档逐条翻译,要么凭感觉想到哪写到哪。结果呢?用例数量不少,但关键场景漏了、边界值没覆盖、重复用例一堆。

测试用例设计的核心问题不是"写多少",而是怎么用最少的用例覆盖最多的场景。等价类划分和边界值分析就是解决这个问题的两把利器。今天这篇文章,我会把这两项技术从原理到实战完整讲透,并补充我在项目中总结的实战技巧。


一、为什么需要等价类划分和边界值分析?

1.1 穷举测试是不现实的

假设一个输入框接受1-100的整数,如果穷举测试,需要测试100个有效值加上无数个无效值。这在时间和资源上根本不可行。

等价类划分和边界值分析的核心思想是:不需要测试每个值,只需要测试每个"等价类"的代表值和"边界"上的值

1.2 缺陷聚集在边界

大量研究表明,软件缺陷往往聚集在输入范围的边界附近。比如:

  • 一个接受1-100的输入框,bug最可能出现在输入0、1、100、101这些值时
  • 一个接受6-10位密码的字段,bug最可能出现在5位、6位、10位、11位时

这就是边界值分析的理论基础:在边界处找bug,效率最高


二、等价类划分:用分组代替穷举

2.1 什么是等价类划分?

等价类划分(Equivalence Partitioning,简称EP)是一种黑盒测试技术,它将输入数据划分为若干个"等价类"——同一等价类中的值,预期会被程序以相同方式处理。因此,每个等价类只需选取一个代表值进行测试

2.2 等价类的分类

输入域
├── 有效等价类:符合需求的输入,程序应正常处理
└── 无效等价类:不符合需求的输入,程序应拒绝或提示错误

2.3 实战案例:披萨订购数量

原文中的经典案例:一个披萨订购系统,数量输入框接受1-10的整数。

不使用等价类: 需要测试1, 2, 3, ..., 10, 0, -1, 11, 12, ..., 99, 100, ... → 无穷无尽

使用等价类划分:

等价类范围代表值预期结果
有效等价类1-105订购成功
无效等价类1小于10提示"数量不能小于1"
无效等价类2大于1015提示"最多订购10份"
无效等价类3非整数3.5提示"请输入整数"
无效等价类4非数字abc提示"请输入数字"

从无穷多个测试值缩减到5个,覆盖率却不降反升——因为每个等价类中的值行为一致,测一个就等于测了全部。

2.4 等价类划分的适用范围

等价类划分不仅适用于数值输入,还适用于:

输入类型划分方式示例
数值范围按范围划分有效/无效年龄0-150:有效(0-150)、无效(负数、>150)
字符串长度按长度划分密码6-20位:有效(6-20)、无效(0-5、21+)
枚举值每个值为一个等价类性别:男、女、其他
集合属于/不属于会员等级:普通/银卡/金卡/钻石
布尔值true/false是否同意协议:是/否

三、边界值分析:在缺陷最密集的地方下功夫

3.1 什么是边界值分析?

边界值分析(Boundary Value Analysis,简称BVA)是对等价类划分的补充。它专门测试等价类边界上的值,因为边界是缺陷最容易出现的地方

3.2 五点边界法

对于一个范围 [min, max],BVA测试以下5个关键点:

值:    min-1   min   min+1   ...   max-1   max   max+1
        ──────┼────┼──────────────┼────┼────┼──────
测试:   ✗      ✓     ✓           ✓    ✓    ✗
        无效   边界  边界内       边界内 边界  无效
测试点取值说明
略小于最小值min-1无效边界,应被拒绝
最小值min有效边界,应被接受
略大于最小值min+1有效值,验证边界内正常
略小于最大值max-1有效值,验证边界内正常
最大值max有效边界,应被接受
略大于最大值max+1无效边界,应被拒绝

3.3 实战案例:密码字段验证

密码字段要求6-10位字符,使用边界值分析:

测试场景输入长度预期结果
5位字符(边界外)"abcde"拒绝,提示"密码至少6位"
6位字符(下边界)"abcdef"接受
7位字符(边界内)"abcdefg"接受
9位字符(边界内)"abcdefghi"接受
10位字符(上边界)"abcdefghij"接受
11位字符(边界外)"abcdefghijk"拒绝,提示"密码最多10位"

我的实战补充: 很多测试人员只测min和max,忽略了min-1和max+1。但恰恰是这两个"刚好越界"的值,最容易暴露出off-by-one错误(差一错误)。比如开发写成了 if len(password) > 6 而不是 >= 6,6位密码就会被错误拒绝。


四、等价类划分 vs 边界值分析:如何配合使用?

两种技术不是替代关系,而是互补关系:

对比维度等价类划分边界值分析
关注点输入的分组分组的边界
数据选取每个等价类取1个代表值取min、min±1、max、max±1
擅长发现功能逻辑错误off-by-one错误、边界处理缺陷
应用顺序先划分等价类再对每个等价类做边界分析
适用场景减少冗余用例捕获边界缺陷

最佳实践:先EP后BVA

Step 1: 用等价类划分确定有效/无效分组
Step 2: 对每个等价类的边界做边界值分析
Step 3: 合并去重,形成最终用例集

五、进阶实战:多字段组合的用例设计

单个字段的等价类和边界值相对简单,但实际项目中往往有多个输入字段需要组合测试。

5.1 案例:用户注册表单

注册表单包含以下字段:

字段规则
用户名4-20位字母数字
密码8-20位,含大小写字母和数字
邮箱合法邮箱格式
手机号11位数字,1开头

Step 1:对每个字段做等价类划分

字段有效等价类无效等价类
用户名4-20位字母数字<4位、>20位、含特殊字符、为空
密码8-20位含大小写+数字<8位、>20位、纯小写、纯数字、为空
邮箱合法格式缺@、缺域名、格式错误、为空
手机号11位1开头非1开头、<11位、>11位、含字母、为空

Step 2:设计测试用例

不需要测试所有组合(4个字段各有5+个等价类,全组合=数千个),而是采用以下策略:

  • 正常场景:所有字段取有效等价类代表值 → 1条用例
  • 单字段异常:每次只有一个字段取无效等价类,其余取有效 → 每个无效等价类1条用例
  • 关键组合:业务上常同时出错的组合 → 少量补充
# 用pytest参数化实现单字段异常测试
@pytest.mark.parametrize("field,value,expected_error", [
    ("username", "abc", "用户名至少4位"),
    ("username", "a" * 21, "用户名最多20位"),
    ("username", "test@user", "用户名只能包含字母和数字"),
    ("password", "Pass12", "密码至少8位"),
    ("password", "alllowercase1", "密码需包含大写字母"),
    ("email", "invalid-email", "邮箱格式不正确"),
    ("phone", "1234567890", "手机号必须11位"),
])
def test_register_invalid_field(field, value, expected_error):
    valid_data = {
        "username": "testuser01",
        "password": "Pass1234",
        "email": "test@example.com",
        "phone": "13800000001"
    }
    valid_data[field] = value
    resp = requests.post("/api/register", json=valid_data)
    assert expected_error in resp.json()["message"]

5.2 鲁棒性边界值分析

标准BVA只测试有效范围边界,鲁棒性BVA(Robust BVA) 还会测试超出范围的值:

测试点取值说明
min-1刚好低于最小值验证系统拒绝明显无效输入
min最小值有效边界
nom中间值正常值
max最大值有效边界
max+1刚好超过最大值验证系统拒绝明显无效输入

我的经验: 鲁棒性BVA在金融、医疗等对数据准确性要求极高的系统中特别重要。比如金额输入框接受0.01-999999.99,不仅要测0.01和999999.99,还要测0.00和1000000.00,确保系统不会接受超出范围的值。


六、常见陷阱与避坑指南

陷阱1:等价类划分不完整

很多人只想到"太大"和"太小"两种无效等价类,忽略了其他类型:

输入:年龄(0-150的整数)
无效等价类:
  ✗ 负数(-1)          ← 大多数人会想到
  ✗ 大于150(200)       ← 大多数人会想到
  ✗ 小数(3.5)          ← 容易遗漏!
  ✗ 非数字(abc)        ← 容易遗漏!
  ✗ 空值("")           ← 容易遗漏!
  ✗ 特殊字符(<script>) ← 容易遗漏!

陷阱2:边界值只取整数边界

如果输入范围是小数呢?比如折扣率0.01-0.99:

边界值:0.00, 0.01, 0.02, 0.98, 0.99, 1.00
不要只测:0, 1, 99, 100(这是整数思维)

陷阱3:忽视输出等价类

等价类划分不仅适用于输入,也适用于输出。比如一个计算折扣的函数:

输入:消费金额
输出:折扣率
  消费 < 100 → 无折扣
  100 ≤ 消费 < 500 → 9折
  500 ≤ 消费 < 1000 → 8折
  消费 ≥ 1000 → 7折

输出等价类边界:99, 100, 499, 500, 999, 1000

陷阱4:过度依赖单一技术

等价类和边界值主要针对单个输入字段。对于多字段组合逻辑、业务流程等场景,还需要配合:

  • 判定表:处理多条件组合
  • 场景法:处理业务流程
  • 状态迁移测试:处理状态变化

七、自动化实践:用pytest实现参数化边界测试

import pytest

class TestDiscountCalculation:
    """折扣计算 - 等价类+边界值自动化测试"""

    def calculate_discount(self, amount):
        """被测函数:根据消费金额计算折扣"""
        if amount < 0:
            raise ValueError("金额不能为负数")
        if amount < 100:
            return 1.0    # 无折扣
        elif amount < 500:
            return 0.9    # 9折
        elif amount < 1000:
            return 0.8    # 8折
        else:
            return 0.7    # 7折

    # 等价类划分测试
    @pytest.mark.parametrize("amount,expected_discount", [
        (50, 1.0),      # 有效等价类:< 100
        (200, 0.9),     # 有效等价类:100-499
        (700, 0.8),     # 有效等价类:500-999
        (1500, 0.7),    # 有效等价类:≥ 1000
    ])
    def test_equivalence_classes(self, amount, expected_discount):
        result = self.calculate_discount(amount)
        assert result == expected_discount

    # 边界值分析测试
    @pytest.mark.parametrize("amount,expected_discount", [
        (0, 1.0),       # 边界:最小值
        (99, 1.0),      # 边界:无折扣上限
        (100, 0.9),     # 边界:9折下限
        (499, 0.9),     # 边界:9折上限
        (500, 0.8),     # 边界:8折下限
        (999, 0.8),     # 边界:8折上限
        (1000, 0.7),    # 边界:7折下限
    ])
    def test_boundary_values(self, amount, expected_discount):
        result = self.calculate_discount(amount)
        assert result == expected_discount

    # 无效等价类测试
    def test_negative_amount(self):
        with pytest.raises(ValueError, match="金额不能为负数"):
            self.calculate_discount(-1)

总结

技术核心思想适用场景关键动作
等价类划分分组取代表减少冗余用例划分有效/无效等价类,每类取1个代表值
边界值分析边界找缺陷捕获off-by-one错误测试min、min±1、max、max±1
鲁棒性BVA越界验证高可靠性系统在BVA基础上增加min-1和max+1

记住这个口诀:先划分等价类,再分析边界值,最后用自动化参数化落地。


分享到:

探索 RunnerGo 全栈测试平台

RunnerGo 是一款面向企业的全栈测试平台,集接口测试、自动化测试、性能测试、UI测试于一体,助力企业提升研发效能。

接口测试
性能测试
自动化测试
UI测试
免费体验 RunnerGo