1. 项目概述:从“背用例”到“造用例”的思维跃迁

干了这么多年测试,我发现一个挺普遍的现象:很多刚入行的朋友,一提到测试用例设计,尤其是像“三角形判定”这种经典例题,第一反应就是去网上搜答案,然后死记硬背那十几二十条用例。面试前猛背一通,工作中遇到类似逻辑,又得从头开始“造轮子”,效率低还容易漏。

这其实陷入了一个误区:把测试当成了背诵题,而不是设计题。今天,我就想用这个最经典的“三角形问题”作为引子,带你彻底摆脱死记硬背。我们不只满足于知道“输入(3,4,5)是直角三角形”,更要搞清楚 为什么 要测这个,以及 如何系统化地 生成所有必要的测试用例。

我们将使用 Python 和 Pytest 这个黄金组合来实战。Python 负责实现核心逻辑和测试数据驱动,Pytest 则提供极其优雅的测试组织和执行框架。目标是让你掌握一套基于白盒测试思想的、可复用的用例设计方法论。以后无论遇到多复杂的业务逻辑,你都能像搭积木一样,有条不紊地设计出高覆盖度的测试用例集。这不仅是为了通过面试,更是为了提升你日常工作的效率和代码质量。

2. 核心思路:白盒测试视角下的三角形问题解构

在开始敲代码之前,我们必须先把思路理清。黑盒测试我们可能只知道输入三边长度,输出三角形类型。但白盒测试要求我们“看到”代码内部的逻辑结构,并据此设计用例。

2.1 三角形判定逻辑的代码实现与路径分析

我们先写一个最基础的三角形判定函数。这是所有测试的靶心。

def triangle_type(a: int, b: int, c: int) -> str:
    """
    判断三角形类型。
    返回: '非三角形', '不等边三角形', '等腰三角形', '等边三角形', '直角三角形'
    注意:此实现存在逻辑缺陷,用于演示测试用例设计。
    """
    # 检查是否为三角形
    if a <= 0 or b <= 0 or c <= 0:
        return "非三角形"
    if not (a + b > c and a + c > b and b + c > a):
        return "非三角形"

    # 判断三角形类型
    if a == b == c:
        return "等边三角形"
    elif a == b or b == c or a == c:
        # 潜在缺陷:等腰直角三角形可能在此被归类为等腰三角形,而错过直角三角形判断
        return "等腰三角形"
    elif a*a + b*b == c*c or a*a + c*c == b*b or b*b + c*c == a*a:
        return "直角三角形"
    else:
        return "不等边三角形"

现在,我们不是用户,而是代码的侦探。我们来画出这段代码的 控制流图 (在脑子里画就行)。关键的逻辑分支点(节点)和走向(边)如下:

  1. 入口
  2. 第一个if :判断任意一边是否<=0。是 -> 走向“非三角形”返回;否 -> 继续。
  3. 第二个if :判断三角不等式。否 -> 走向“非三角形”返回;是 -> 继续。
  4. 第三个if :判断是否三边相等。是 -> 返回“等边三角形”;否 -> 继续。
  5. elif :判断是否任意两边相等。是 -> 返回“等腰三角形”;否 -> 继续。
  6. elif :判断是否满足勾股定理。是 -> 返回“直角三角形”;否 -> 继续。
  7. else :返回“不等边三角形”。

白盒测试的核心覆盖标准之一就是 路径覆盖 ,即尽可能让测试用例执行到所有可能的逻辑路径。从上面的分析可以看出,从入口到出口,存在多条路径,例如:路径A(边长<=0 -> 非三角),路径B(三角不等式不成立 -> 非三角),路径C(三边相等 -> 等边),路径D(两边相等 & 非等边 -> 等腰),路径E(满足勾股定理 & 非等腰 -> 直角),路径F(不满足以上任何特殊条件 -> 不等边)。

我们的用例设计,就要有针对性地覆盖这些路径。但请注意,我故意在代码里留了一个缺陷:判断等腰在判断直角之前。这意味着对于像(3,3,3√2)这样的等腰直角三角形,代码会将其判定为“等腰三角形”,而永远不会走到直角三角形的判断逻辑。这是一个典型的 逻辑顺序缺陷 ,也是我们测试需要发现的重点。

2.2 测试策略:从覆盖标准到用例矩阵

死记硬背的用例是散乱的点,而我们需要的是一个系统化的生成矩阵。基于上面的路径分析,我们可以结合多种白盒测试覆盖标准来设计用例:

  • 语句覆盖 :每个语句至少执行一次。我们需要用例能走到所有返回语句。
  • 分支覆盖(判定覆盖) :每个逻辑判断的True和False分支至少各走一次。例如 a<=0 or b<=0 or c<=0 这个条件,我们需要用例使其为True,也需要用例使其为False。
  • 条件覆盖 :每个逻辑条件中的每个子条件(如a<=0, b<=0, c<=0)都分别取到True和False。这比分支覆盖更细致。
  • 路径覆盖 :覆盖所有可能的执行路径。对于复杂逻辑,路径可能爆炸,但三角形问题相对简单,我们可以追求主要路径覆盖。

最实用的方法是 构建一个“条件-动作”表 ,也叫判定表。但我们今天用一个更直观的“用例分类矩阵”来驱动我们的测试数据。

我们围绕几个核心“维度”来组合测试数据:

  1. 边长有效性维度 :零、负数、正数。
  2. 三角形构成维度 :满足三角不等式、不满足(两边之和等于/小于第三边)。
  3. 三角形类型维度 :等边、等腰(非等边)、直角(非等腰)、不等边。
  4. 边界值维度 :刚好等于边界的情况(如两边之和等于第三边、边长为1等)。
  5. 缺陷触发维度 :专门针对我们怀疑的缺陷设计数据(如等腰直角三角形)。

这样组合下来,你就能系统性地生成用例,而不是东一榔头西一棒子。接下来,我们用Pytest把这个矩阵实现出来。

3. 实战:用Pytest实现数据驱动的高覆盖测试套件

Pytest的强大之处在于其简洁的夹具(Fixture)系统和参数化测试功能,非常适合实现数据驱动的测试。我们不会写几十个重复的 test_ 函数,而是通过一个优雅的结构来管理所有测试数据。

3.1 环境搭建与测试结构规划

首先确保你安装了pytest: pip install pytest -U

创建一个项目目录,例如 triangle_test 。里面至少有两个文件:

  • triangle.py :存放上面的 triangle_type 函数。
  • test_triangle.py :存放我们所有的测试用例。

我们不把测试数据硬编码在测试函数里,而是准备用一个独立的数据模块或夹具来管理,这样更清晰,也利于复用。这里我采用在测试文件内定义数据的方式。

3.2 构建参数化测试数据矩阵

test_triangle.py 中,我们首先定义测试数据。数据组织方式决定了测试的清晰度和可维护性。

import pytest
from triangle import triangle_type

# 核心测试数据矩阵,格式:(a, b, c, expected_result, test_case_description)
TEST_CASE_MATRIX = [
    # 维度1: 无效输入(非正数)
    (0, 4, 5, "非三角形", "边a为0"),
    (-1, 4, 5, "非三角形", "边a为负数"),
    (3, 0, 5, "非三角形", "边b为0"),
    (3, -4, 5, "非三角形", "边b为负数"),
    (3, 4, 0, "非三角形", "边c为0"),
    (3, 4, -5, "非三角形", "边c为负数"),
    (0, 0, 0, "非三角形", "三边均为0"),

    # 维度2: 不构成三角形(违反三角不等式)
    (1, 2, 3, "非三角形", "两边之和等于第三边 (1+2=3)"),
    (1, 2, 4, "非三角形", "两边之和小于第三边 (1+2<4)"),
    (5, 1, 2, "非三角形", "任意两边之和小于第三边 (1+2<5)"),
    (1, 5, 2, "非三角形", "任意两边之和小于第三边 (1+2<5)"),

    # 维度3 & 4: 有效三角形 - 边界与类型组合
    # 等边三角形
    (1, 1, 1, "等边三角形", "最小等边三角形(边界值)"),
    (100, 100, 100, "等边三角形", "大数等边三角形"),
    # 等腰三角形 (非等边)
    (3, 3, 5, "等腰三角形", "等腰三角形,腰为3,底为5"),
    (5, 8, 5, "等腰三角形", "等腰三角形,腰为5,底为8"),
    (7, 5, 5, "等腰三角形", "等腰三角形,腰为5,底为7"),
    (2, 2, 3, "等腰三角形", "小等腰三角形"),
    # 直角三角形 (非等腰)
    (3, 4, 5, "直角三角形", "经典直角三角形 (3-4-5)"),
    (5, 12, 13, "直角三角形", "常见直角三角形 (5-12-13)"),
    (8, 15, 17, "直角三角形", "常见直角三角形 (8-15-17)"),
    # 不等边三角形 (非直角非等腰)
    (3, 6, 7, "不等边三角形", "典型不等边三角形"),
    (10, 11, 12, "不等边三角形", "大数不等边三角形"),

    # 维度5: 针对潜在缺陷的测试 (等腰直角三角形)
    # 根据当前有缺陷的逻辑,它会返回“等腰三角形”,但我们的期望值应该是“直角三角形”吗?
    # 不,我们测试的是“当前代码的行为”,所以期望值应设为代码实际会输出的“等腰三角形”。
    # 这个用例恰恰能暴露缺陷!因为从业务逻辑上,它应该是直角三角形。
    (3, 3, (18**0.5), "等腰三角形", "等腰直角三角形 (3,3,√18≈4.24) - 用于暴露逻辑顺序缺陷"),
]

为什么这样组织数据?

  1. 分组清晰 :按测试维度分组,一目了然。新增用例时,很容易找到该放的位置。
  2. 信息完整 :每个用例是一个元组,包含输入 (a,b,c) 、期望输出和描述。描述很重要,当测试失败时,pytest会显示它,你能立刻知道是哪个场景出了问题。
  3. 暴露缺陷 :最后一组数据是“侦探用例”,专门验证我们的怀疑。注意,这里期望值填写的是当前有缺陷代码的 实际输出 (“等腰三角形”)。当我们修复代码后,需要将此期望值改为“直角三角形”,这个测试用例就会从“验证缺陷存在”变为“验证缺陷已修复”。

3.3 编写参数化测试函数与使用夹具

有了数据矩阵,编写测试函数就非常简单了。我们使用 @pytest.mark.parametrize 装饰器。

@pytest.mark.parametrize("a, b, c, expected, description", TEST_CASE_MATRIX)
def test_triangle_type_parametrized(a, b, c, expected, description):
    """
    参数化测试:验证各种输入下的三角形类型判定。
    使用description作为测试ID的一部分,便于识别。
    """
    # 注意:对于浮点数输入(如等腰直角三角形),我们需要处理精度问题
    if isinstance(c, float):
        # 在实际比较时,可能需要四舍五入或使用近似比较,这里简化处理,直接传入。
        # 更严谨的做法是使用pytest.approx
        result = triangle_type(a, b, c)
    else:
        result = triangle_type(a, b, c)
    assert result == expected, f"用例‘{description}’失败: 输入({a},{b},{c}), 期望‘{expected}’, 实际‘{result}’"

运行测试:在项目根目录下执行 pytest -v -v 参数显示详细信息,你会看到每个用例都被单独执行和报告。

但是,这里有个问题 :我们的 triangle_type 函数声明接收 int ,但测试数据中出现了浮点数 (18**0.5) 。这会导致类型错误或者精度判断错误。这引出了两个重要的实操要点:

  1. 接口契约 :函数设计时是否应该支持浮点数?如果支持,如何比较浮点数的相等(勾股定理判断)?这是一个设计决策。为了简化,我们可以先修改函数和测试用例,全部使用整数。例如,等腰直角三角形可以用 (1,1,√2) ,但√2不是整数。我们可以用 (3,4,5) 是直角, (3,3,5) 是等腰,来测试分支顺序。或者,我们修改函数,使用 math.isclose 进行浮点数比较。
  2. 测试的健壮性 :测试本身要适应被测试代码的变化。我们可以创建一个**夹具(Fixture)**来预处理输入数据,或者将浮点数用例单独处理。

让我们调整一下,采用更严谨的方式,并引入夹具来处理可能的设置和清理(虽然这个简单例子不需要清理)。

import math
import pytest
from triangle import triangle_type

# 修改测试数据,全部使用整数,并构造一个整数边长的等腰直角三角形用例。
# 是否存在整数边长的等腰直角三角形?有,例如(1,1,√2)不是整数。
# 但我们可以用其他方式测试分支顺序。我们用一个“等腰非直角”和“直角非等腰”来覆盖两个分支即可。
# 我们新增一个用例:输入(3,4,5)是直角,输入(3,3,5)是等腰。这已经能覆盖两个不同分支。
# 为了测试缺陷,我们需要一个“既是等腰又是直角”的用例,但整数边长下不存在。
# 因此,我们暂时注释掉那个浮点数用例,先专注于逻辑覆盖。

TEST_CASE_MATRIX = [
    # ... 其他用例保持不变 ...
    # (3, 3, (18**0.5), "等腰三角形", "等腰直角三角形..."), # 暂时注释
]

# 我们可以创建一个夹具,用于未来可能需要的共享资源,比如一个临时配置文件。
@pytest.fixture(scope="function")
def clean_triangle_logic():
    """
    一个示例夹具,每个测试函数运行前执行。
    这里虽然只是演示,但在复杂场景下,可以用于准备测试环境、重置状态等。
    """
    print("\n--- 开始执行新的三角形测试 ---")
    yield # 测试函数在此处执行
    print("--- 三角形测试执行完毕 ---")

@pytest.mark.parametrize("a, b, c, expected, description", TEST_CASE_MATRIX)
def test_triangle_type_parametrized(a, b, c, expected, description, clean_triangle_logic):
    """
    集成夹具的参数化测试。
    """
    result = triangle_type(a, b, c)
    assert result == expected, f"用例‘{description}’失败: 输入({a},{b},{c}), 期望‘{expected}’, 实际‘{result}’"

# 单独测试浮点数精度场景(如果我们决定修改函数支持浮点数)
def test_triangle_type_with_float():
    """
    测试浮点数输入下的判定。
    假设我们修改了triangle_type函数,使用math.isclose进行近似比较。
    """
    # 假设这是修改后的函数调用
    # 这里仅作演示,我们不对原函数做修改。
    # 正确的做法是:在判断勾股定理时,使用 math.isclose(a*a + b*b, c*c, rel_tol=1e-9)
    pass

现在运行 pytest -v ,你应该能看到所有测试用例(除了浮点数那个)都被执行,并且由于我们代码有缺陷,可能所有测试都通过(因为浮点数用例被注释了)。等等,这不对。我们的代码缺陷(等腰在直角前判断)会影响 (3,4,5) 这样的纯直角三角形吗?不会,因为 (3,4,5) 不是等腰。它会影响 (5,5,5√2) 吗?会,但我们没用这个用例。所以目前测试集可能全部通过,但这不意味着代码没缺陷,只是我们的用例没覆盖到那个缺陷分支。

这就是白盒测试设计的重要性: 你必须根据代码逻辑设计能覆盖不同分支组合的用例 。我们需要增加一个用例,其输入 同时满足 等腰和直角条件,但期望的业务结果是“直角三角形”。由于整数边长不存在,我们有两种选择:1) 修改函数支持浮点数比较;2) 暂时用这个用例来“期望”当前有缺陷的输出(等腰),待修复后再改期望值。我们选择第二种,作为“缺陷测试用例”加入矩阵。

让我们取消注释并修改那个用例,明确其目的:

    # 维度5: 缺陷触发测试 - 针对“等腰判断先于直角判断”的逻辑顺序缺陷
    # 当前代码版本下,此输入会被错误地判定为“等腰三角形”。
    # 因此,期望值设置为当前错误的输出,用于验证缺陷存在。
    # 当开发修复bug(调整判断顺序)后,此用例将失败,我们需要将其期望值改为“直角三角形”。
    (3, 3, (18**0.5), "等腰三角形", "[缺陷验证]等腰直角三角形应被识别为直角,但当前版本判为等腰"),

现在,这个用例的期望值 ”等腰三角形“ 与代码当前行为一致,所以测试会通过。这记录了这个已知缺陷。未来修复后,这个测试将失败,提示我们需要更新期望值。这是一种管理“已知bug”的测试方法。

4. 深入:覆盖度分析与测试报告生成

编写了大量用例,我们如何衡量其质量?覆盖度(Coverage)是一个关键指标。我们可以使用 pytest-cov 插件来统计测试对代码的覆盖情况。

安装: pip install pytest-cov

运行覆盖度检查: pytest --cov=triangle --cov-report=term-missing

--cov=triangle 指定要分析覆盖度的模块(我们的 triangle.py )。 --cov-report=term-missing 在终端输出报告,并显示哪些行未被覆盖。

执行后,你会看到类似这样的输出:

Name          Stmts   Miss  Cover   Missing
-------------------------------------------
triangle.py      15      0   100%
-------------------------------------------
TOTAL            15      0   100%

看起来是100%覆盖!但这就是“充分测试”了吗? 远远不是! 这就是白盒测试的一个经典陷阱: 覆盖度达标不等于没bug 。我们的语句覆盖是100%,因为每个语句都执行了。分支覆盖呢?可能也接近100%。但那个逻辑顺序缺陷依然存在。因为覆盖度工具只跟踪了哪行代码被执行了,它不知道 elif a == b or ... elif a*a + b*b == c*c ... 这两个分支的顺序在业务逻辑上是错误的。

所以,覆盖度是一个必要的、有用的量化工具,但它不是充分条件。高覆盖度能帮你发现“未测试的代码”,但无法保证“测试用例的正确性”和“业务逻辑的合理性”。必须结合精心设计的、基于业务规则和逻辑路径的用例。

4.1 分支覆盖与条件组合

为了更彻底,我们可以追求 条件组合覆盖 。例如,在判断 a == b or b == c or a == c 时,有三个子条件。完整的条件组合覆盖需要测试:

  • a==b True, b==c False, a==c False (例如 3,3,4)
  • a==b False, b==c True, a==c False (例如 4,3,3)
  • a==b False, b==c False, a==c True (例如 3,4,3)
  • a==b False, b==c False, a==c False (例如 3,4,5)

我们的用例矩阵已经覆盖了这些情况。同样,对于勾股定理判断 a*a + b*b == c*c or ... ,也有三个子条件,需要分别测试直角边是(a,b), (a,c), (b,c)对应斜边的情况。我们的用例(3,4,5)覆盖了 a*a+b*b==c*c ,还需要补充如(5,12,13)可能覆盖的是 a*a+b*b==c*c (如果a=5,b=12,c=13),但最好能明确覆盖到 a*a + c*c == b*b b*b + c*c == a*a 的场景。例如:

  • 输入(5, 13, 12):应满足 5 5 + 12 12 = 13*13,即 a*a + c*c == b*b
  • 输入(13, 5, 12):应满足 5 5 + 12 12 = 13*13,即 b*b + c*c == a*a

把这些用例加到矩阵里,你的测试就更加完备了。

    # 补充直角三角形不同边序的测试,覆盖勾股定理判断的所有子条件
    (5, 12, 13, "直角三角形", "直角三角形 (5-12-13), 覆盖 a*a + b*b == c*c"),
    (5, 13, 12, "直角三角形", "直角三角形 (5-12-13), 覆盖 a*a + c*c == b*b"),
    (13, 5, 12, "直角三角形", "直角三角形 (5-12-13), 覆盖 b*b + c*c == a*a"),

4.2 使用Pytest的标记(Mark)进行分类测试

当用例越来越多时,你可能只想运行某一类测试。Pytest的 @pytest.mark 装饰器可以帮我们给测试打标签。

import pytest

# 在参数化装饰器上添加自定义标记
@pytest.mark.parametrize("a, b, c, expected, description", TEST_CASE_MATRIX)
@pytest.mark.triangle  # 自定义一个标记
def test_triangle_type_parametrized(a, b, c, expected, description):
    # ... 实现 ...

# 单独标记某个测试函数
@pytest.mark.slow
def test_large_number_triangle():
    """测试大数运算,可能较慢"""
    assert triangle_type(1000000, 1000000, 1000000) == "等边三角形"

然后你可以通过标记来运行测试:

  • pytest -m triangle :只运行标记为 triangle 的测试。
  • pytest -m "not slow" :运行除了标记为 slow 以外的所有测试。

这在大规模测试套件中非常有用,可以快速执行冒烟测试、回归测试等。

5. 常见问题、排查技巧与经验实录

在实际操作中,你会遇到各种各样的问题。这里记录一些典型场景和我的处理经验。

5.1 测试数据管理:维护性与可读性

问题 :当测试用例成百上千时, TEST_CASE_MATRIX 列表会变得非常庞大,难以维护和阅读。 解决方案

  1. 外部数据文件 :将测试数据存储在CSV、JSON或YAML文件中。例如 test_data.json
    [
      {"a": 0, "b": 4, "c": 5, "expected": "非三角形", "desc": "边a为0"},
      ...
    ]
    
    在测试中读取文件:
    import json
    import os
    
    def load_test_data():
        with open(os.path.join(os.path.dirname(__file__), 'test_data.json'), 'r') as f:
            return [tuple(d.values()) for d in json.load(f)] # 转换为元组列表
    
    @pytest.mark.parametrize("a,b,c,expected,desc", load_test_data())
    def test_triangle(..., desc): ...
    
  2. 使用 pytest_generate_tests 钩子 :对于更动态的数据生成,可以使用此钩子函数。这在需要根据某些规则批量生成测试参数时特别有用。
  3. 保持清晰的命名和分组 :即使在列表里,也要用注释严格分组,并为每个用例提供自解释的描述字符串。

5.2 浮点数比较的坑

问题 :判断直角三角形时,计算 a*a + b*b == c*c 对于浮点数几乎总是False,因为存在精度误差。 解决方案 :永远不要用 == 直接比较浮点数。使用 math.isclose pytest.approx

import math

def is_right_triangle(a, b, c):
    # 对三边进行排序,方便找出最长边(斜边)
    sides = sorted([a, b, c])
    return math.isclose(sides[0]**2 + sides[1]**2, sides[2]**2, rel_tol=1e-9)

在测试中,如果预期结果是浮点数计算,也用 approx

import pytest
result = some_function_returning_float()
assert result == pytest.approx(expected_value, rel=1e-9)

5.3 测试失败时的调试信息

问题 :一个参数化测试失败,只显示一堆数字,很难快速定位是哪个具体场景出了问题。 解决方案 :充分利用 pytest.param ids 参数。

# 使用pytest.param包装用例,并指定id
test_cases = [
    pytest.param(0, 4, 5, "非三角形", id="invalid_zero_a"),
    pytest.param(3, 4, 5, "直角三角形", id="right_3_4_5"),
    # id会自动显示在测试输出中
]

@pytest.mark.parametrize("a,b,c,expected", test_cases)
def test_triangle(a,b,c,expected):
    ...

或者,在参数化装饰器中直接使用 ids 列表:

@pytest.mark.parametrize(
    "a,b,c,expected",
    [(0,4,5,"非三角形"), (3,4,5,"直角三角形")],
    ids=["零边测试", "经典直角测试"] # 这个列表长度必须和参数列表长度一致
)
def test_triangle(a,b,c,expected):
    ...

运行测试时,你会看到清晰的测试名,而不是参数值。

5.4 测试夹具(Fixture)的作用域与生命周期

问题 :不恰当地使用 scope function , class , module , session )会导致测试间状态污染或效率低下。 经验

  • scope='function' (默认):每个测试函数都重新初始化一次。最安全,隔离性好。
  • scope='module' :同一个 .py 文件中的所有测试函数共享一个夹具实例,只初始化一次。适合初始化耗时但只读的资源(如读取大型配置文件、建立数据库连接池)。
  • scope='session' :整个测试会话(一次 pytest 命令执行)只初始化一次。适合全局性、昂贵的资源。
  • 黄金法则 :除非有明确的性能需求且能保证状态安全,否则优先使用 function 作用域。如果使用 module session 作用域,务必确保夹具返回的数据是不可变的,或者每个测试都会获取自己的副本,避免测试间相互影响。

5.5 测试的独立性与幂等性

原则 :每个测试用例都应该是独立的,不依赖于其他用例的执行顺序,也不依赖于外部状态。执行一次和执行多次结果应该一样(幂等)。 检查点

  • 测试是否修改了全局变量或类属性?
  • 测试是否依赖一个特定的文件系统状态或数据库记录?
  • 测试用例之间是否有隐藏的顺序依赖?(Pytest默认发现测试的顺序可能因操作系统、文件系统而异) 确保独立性的方法 :使用夹具在测试开始前设置( setup )一个已知的干净状态,在测试结束后清理( teardown )。 pytest fixture 通过 yield 语句可以很方便地实现 setup/teardown
@pytest.fixture
def clean_database():
    # Setup: 连接到测试数据库,清空相关表
    db = connect_to_test_db()
    db.clear_table('users')
    yield db  # 测试函数在此处执行,db对象作为参数传入
    # Teardown: 测试后再次清理(可选,因为yield前已经清空,但确保万无一失)
    db.clear_table('users')
    db.close()

通过这套方法,你将不再需要死记硬背任何测试用例。面对任何新的函数或模块,你的思维流程将是:1) 理解需求和代码逻辑;2) 分析输入输出域及逻辑路径;3) 运用等价类、边界值、判定表等方法设计测试数据;4) 使用Pytest的参数化、夹具等功能高效实现测试套件;5) 利用覆盖度工具查漏补缺,并始终牢记覆盖度不是质量的唯一标准。这个过程本身,就是测试工程师核心价值的体现。

更多推荐