你拿到人生第一套房子的钥匙,第一件事不是急着买沙发,而是请一位“首席验收官”来把关——他拿着厚厚一沓检查单,逐项查墙面平不平、水管漏不漏、电路通不通,每过一项盖个“通过”章,不合格就亮红牌,还会写清楚到底是哪面墙哪个角落出了问题。验收不过,业主可不敢收房。 软件开发也是一样的道理:代码写完,不能拍胸脯说“我觉得没问题”,要让 pytest 这位验收官替你逐项检查。它是 Python 生态中最流行的测试框架,写起来简单、跑起来飞快,还自带断言、夹具(fixture)、参数化、标记过滤、插件生态一整套工具。本文就用“房屋竣工验收”这条主线,手把手带你从零搭建一套属于自己的代码验收体系,跑通 CI 流水线,生成漂亮的测试报告。

岗位关联

对 AI 从业者而言,测试是工程化的安全网。掌握 pytest,意味着你交付的模型、数据管道和训练代码都可验证、可回归、可协作,这是从“调参侠”走向“专业工程师”的标配能力。

目录
  1. 可视化演示
  2. 初识pytest:请来首席验收官
  3. 断言写作:验收标准怎么写
  4. fixture机制:标准工具包
  5. 参数化测试:一套标准,多户复验
  6. 标记过滤:给检查单贴便签
  7. 插件生态:外聘专家团
  8. 工程实践:竣工验收总方案
  9. 调试优化:验收提速与复查
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

动态演示pytest实战的核心机制,通过开始/执行/监控/完成四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

pytest实战:pyte pytest安装配置与测试发现 assert断言语法与异常测试 核心机制可视化 pytest实战整体架构与关键组件

初识pytest:请来首席验收官

你拿到毛坯房钥匙,第一件事不是急着添置家具,而是请一位首席验收官。这位验收官要上岗,得先领“工作证”(安装工具),还得懂行规——知道哪些房间要查、检查项写在哪里。软件工程也一样,代码写完后,需要 pytest 这位验收官按约定把检查项找出来,逐项过堂。

pytest 是 Python 圈最流行的测试框架,“上岗”只需一条命令:pip install pytest。它有几条硬约定:测试文件取名 test_*.py(或 *_test.py),测试函数以 test_ 开头。这些约定就像“户型图上的标识”——验收官扫一眼就知道哪里藏着检查项,不需要你额外指路。

在项目根目录敲下 pytest,它会自动收集所有符合条件的用例并逐个执行,最终给出 N passed 或 N failed 的总结。再进一步,你还可以关心“验收面积”——即覆盖率:验收官到底检查了整栋楼的多少房间、多少条走廊。覆盖率越高,漏掉问题的可能性越低。

装上验收官、立好约定,后续每加一个新功能,只要补一条 test_ 用例,它就会自动出现在下一次验收清单里。从此再也不用担心“改好了东墙,拆塌了西墙”。

覆盖率=已执行代码行数总代码行数×100%\text{覆盖率} = \frac{\text{已执行代码行数}}{\text{总代码行数}} \times 100\%
覆盖率就是“验收面积占比”:你检查了多少行代码,除以整个模块一共有多少行代码。
  1. 已执行代码行数这是验收官真正走到、检查到的代码行,是公式的分子。
    已执行代码行数\text{已执行代码行数}
  2. 总代码行数整个被测模块的全部代码行,作为公式的分母基准。
    总代码行数\text{总代码行数}
  3. 乘以 100%把 0 到 1 的小数换算成更直观的百分数,方便设定质量红线。
    ×100%\times 100\%

断言写作:验收标准怎么写

验收官手上不能只有空白检查单,每一项检查都必须有“合格线”。比如墙面垂直度误差小于 5mm,水管加压 10 分钟不渗漏。标准写得越具体,检查越有说服力——“差不多”“还行”这种模糊话,在验收单上是没有位置的。

在 pytest 里,“合格线”就是一条 assert 语句。assert 后面跟一个布尔表达式,表达式为 True 就通过,为 False 就失败。更强的是,失败时 pytest 自动把“我实际得到什么值”和“期望得到什么值”写进报告,相当于验收官在红牌上注明“西墙垂直度偏差 12mm,合格线 5mm”。

实际工作中常用三类断言:普通断言 assert x > 0 检查大小关系;异常断言 with pytest.raises(ValueError) 期望函数抛出指定异常;近似断言 pytest.approx(3.14) 解决浮点数精度问题。范围检查则可以连写,比如判断一个评分是否落在合法区间内:

合格线写得越细,施工队越不敢糊弄;断言写得越具体,程序员的代码越不容易出幺蛾子。把验收标准养成分层的习惯,测试报告本身就成了一份活的“质量验收手册”。

0≤x≤100 \leq x \leq 10
这等价于“x 既大于等于 0,又小于等于 10”,是验收官对某个指标划出的上下边界。
  1. 左边界 0指标的下限,低于 0 即为不合格,例如评分不能为负。
    0≤x0 \leq x
  2. 右边界 10指标的上限,超过 10 即为不合格,例如评分满分是 10。
    x≤10x \leq 10
  3. 区间合并两个条件同时成立,x 才落在合法验收区间内。
    0≤x≤100 \leq x \leq 10

fixture机制:标准工具包

验收官干活不能空手:测墙要用激光水平仪,测水要用加压泵,测电要用万用表。而且不同房间、不同阶段还会反复用到同套工具。这些“标准工具包”,在 pytest 里就叫 fixture。

fixture 用 @pytest.fixture 装饰一个普通函数,然后作为参数“注入”测试函数。你在测试函数签名里写 cart,pytest 就自动找到名为 cart 的 fixture,把它的返回值递进来。这种“你只管要,框架负责给”的写法叫依赖注入——省去了手动准备数据的样板代码。

fixture 还有几个进阶用法:scope="module" 让工具包整个模块只造一次,节省开销;yield 把函数劈成两半,前半段是准备(setup),后半段是清理(teardown),测试结束自动收尾;autouse=True 则让工具“自动佩戴”,不需要每个用例主动打招呼。它的生命周期可以简单表示如下:

工具包统一管理之后,验收官不再手忙脚乱,团队协作时也不会“你拿你的尺、我拿我的秤”。任何测试用例需要数据供应,只要声明一声,pytest 自动递上,干净又利落。

setup→test→teardown\text{setup} \rightarrow \text{test} \rightarrow \text{teardown}
fixture 的三段式流程:yield 之前做准备工作,中间跑测试,yield 之后做清理,像打扫干净房间再交还给业主。
  1. setup 准备阶段yield 之前的代码负责搭环境,比如连接数据库、构造默认数据。
    setup\text{setup}
  2. test 测试执行注入的 fixture 值在测试函数里被使用,测试主体在这一阶段运行。
    test\text{test}
  3. teardown 清理阶段yield 之后的代码负责释放资源,比如关闭连接、删除临时文件。
    teardown\text{teardown}

参数化测试:一套标准,多户复验

一栋楼里往往有十几户相同户型。验收官不会每户都重写一遍检查单,而是把同一套检查单套用到每一户,只是每户的“期望值”略有不同——比如一层采光差,照度要求低一些;顶层风大,窗户密闭性要求高一些。

在 pytest 里,这套“复验单”就是 @pytest.mark.parametrize。它把同一个测试函数变成多条独立用例记录,每组参数跑一遍。参数化最大的价值是:所有数据组合都会执行完毕,而不是像 for 循环那样遇到第一组失败就中断,后面的数据白白被跳过。

当多个参数装饰器叠在一起时,pytest 会取它们的“笛卡尔积”——也就是把所有可能组合都生成一条用例。ids 参数还能给每条用例起个可读的名字,失败报告里一眼就能看清是哪组数据出了问题。参数化还可以与 fixture 联动,比如登录账号由 fixture 提供,各项输入数据由参数化注入。每户复验需要检查的组合总数就是:

一套检查单跑全楼,既省了重复抄写的功夫,又避免了漏查哪一户。参数化是测试用例的“复制器”,一份逻辑,千份覆盖,性价比极高。

N=m×nN = m \times n
如果一栋楼有 m 种户型,每种户型要检查 n 个验收项,那么总检查项就是两者相乘。
  1. m 种户型参数化中第一组参数的取值个数。
    mm
  2. n 个验收项参数化中第二组参数的取值个数。
    nn
  3. 组合总数 N两组参数的笛卡尔积,即所有可能组合的数量。
    m×nm \times n

标记过滤:给检查单贴便签

厚厚的验收清单里,红色便签标注“消防专项”,蓝色便签标注“水电专项”。今天业主要求只查消防,验收官只需把红色便签的项抽出来,不必从第一页翻到最后一页。

pytest 里的“便签”就是标记(mark)。@pytest.mark.slow 给用例贴上“慢”标签,运行 pytest -m slow 只跑慢用例,pytest -m "not slow" 则反过来排除它们。自定义标记记得先在 pytest.ini 里注册,避免拼写警告,就像给便签提前准备好一盒分类标签。

内置标记同样实用:@pytest.mark.skip(reason="接口未上线") 无条件跳过;@pytest.mark.skipif(条件, reason=...) 条件成立才跳过,比如 sys.platform == "win32" 时跳过 Linux 专属测试;@pytest.mark.xfail 则标记“预期失败”——挂了不算错,反而说明 bug 如预期存在。用数学语言表达“条件跳过”的逻辑是这样的:

便签让验收官能按优先级和时间灵活安排检查:紧急回归时只跑关键组,全量验收留到发布前。测试的“分层运营”,靠的就是这几张小小的彩色便签。

execute(t)={skipp≤0runp>0\text{execute}(t) =\begin{cases}\text{skip} & p \leq 0 \\\text{run} & p > 0\end{cases}
这是验收官的一个分支判断:p 不满足条件就跳过,满足条件才真正执行。
  1. 条件 p ≤ 0判断条件不成立,验收官直接贴“跳过”标签,不进入检查流程。
    skipp≤0\text{skip} \quad p \leq 0
  2. 条件 p > 0判断条件成立,验收官正常执行这条用例。
    runp>0\text{run} \quad p > 0
  3. 分段选择整个表达式描述了“条件决定行为”的规则,对应 skipif 装饰器。
    execute(t)\text{execute}(t)

插件生态:外聘专家团

甲醛浓度、防雷接地这类检测太专业,普通验收官不擅长,于是物业外聘专家团队,各带专业仪器入场。有的测空气、有的测辐射、有的测结构沉降——这些“外聘专家”就是 pytest 的插件。

pytest 的插件生态成熟且安装方便:pytest-cov 是覆盖率专家,统计代码被测试执行的比例;pytest-xdist 是提速专家,-n auto 把用例分发到多核 CPU 同时跑;pytest-mock 是模拟专家,测试时替换外部接口,不依赖真实网络;pytest-rerunfailures 则是不稳定用例的“复查专家”。而 conftest.py 像一个“共享工具房”,里面的 fixture 和钩子对所有测试文件可见,外聘专家也在这里集合待命。

插件通过“钩子”深度嵌入测试生命周期,例如 pytest_runtest_setup 在每条用例执行前触发。不管接入多少插件,最终报告的本质都是一种汇总统计——每个用例的结果被收进总账:

验收团队从“一人多能”升级为“专家会诊”,工程质量自然更有底气。更重要的是,插件作为标准件可以被任意项目复用——今天选择的工具,明天换一个项目依然管用。

report=∑t∈casesresult(t)\text{report} = \sum_{t \in \text{cases}} \text{result}(t)
报告就是所有测试用例结果的加总:通过一条记一分,失败一条记一次,最终汇成红绿统计。
  1. cases 用例集合本轮验收覆盖的全部测试用例。
    t∈casest \in \text{cases}
  2. result(t) 单条结果每一条用例执行后的结果(通过、失败、跳过)。
    result(t)\text{result}(t)
  3. 求和符号把所有单条结果汇总成一份总报告,方便人眼和 CI 系统判断。
    ∑\sum

工程实践:竣工验收总方案

单套房间验收容易,整栋楼竣工验收就要讲统筹了:先验结构,再验水电,最后验精装;每一步都要留档、签字、追责。软件项目的测试同样需要一套“总方案”,这正是从小作坊走向工程化的标志。

工程级 pytest 实践通常包括四件事:第一,目录规划——用 tests/ 目录按业务模块划分子目录,测试代码与业务代码分离;第二,配置文件——在 pytest.ini 里集中声明 testpaths、注册标记、设定默认命令行参数;第三,CI 集成——在 GitHub Actions 或 GitLab CI 中配一条流水线,每次提交代码自动 pip install -e . && pytest;第四,报告生成——用 --html=report.html 或接入 Allure 输出可视化测试报告,让团队一眼看到质量趋势。

CI 流水线最核心的价值是“自动巡检”。它像一个不知疲倦的机器人,每收到一次代码提交就跑一遍全量验收,并执行质量门禁检查:

有了“整体验收方案”,任何人提交代码都要先过巡检机器人,主干分支永远保持绿色可用状态。工程化不是抽象口号,它落在这张门禁公式上,落在每一次 push 被拦下的那一刻。

gate=全部测试通过∧覆盖率≥80%\text{gate} = \text{全部测试通过} \wedge \text{覆盖率} \geq 80\%
门禁公式:测试全绿且覆盖率达标,这次提交才算“验收合格”,否则自动拦住合并。
  1. all pass 全部通过测试套件中没有失败用例,是合并代码的基础条件。
    全部测试通过\text{全部测试通过}
  2. 覆盖率 ≥ 80%足够多的代码被测试覆盖,防止“表面全绿、核心裸奔”。
    覆盖率≥80%\text{覆盖率} \geq 80\%
  3. 逻辑与两个条件同时满足才算合格,缺一不可。
    ∧\wedge

调试优化:验收提速与复查

验收官再专业,一天也只能验几套房,业主催进度怎么办?加人手并行查、对“幽灵问题”复查一遍——这就是调试与提速的朴素智慧。

pytest 的提速和调优工具正好对应这些招数:-n auto 并行跑用例,相当于多派几个验收官同时进场;--reruns 2 对偶发失败的用例自动重试,减少“误伤”——就像复测一次才发现墙面其实达标;--timeout=60 给每条约 1 分钟上限,卡死的用例自动判失败,不至于一个坏灯泡拖住整栋楼;pytest-randomly 打乱用例顺序,专门暴露那些“必须按顺序跑才过”的隐藏耦合。

提速不是盲目的,总耗时可以被一个简单公式描述。假设单次运行时间为 t,偶发失败率为 r,每次重试要额外花 k 倍的单次时间,那么总耗时大约是:

又快又稳的验收团队,让交付节奏从“害怕回归”走向“放心重构”。并行、重试、超时、随机顺序这四件套,共同守住了测试过程的“体验底线”。

Ttotal=t×(1+r×k)T_{\text{total}} = t \times (1 + r \times k)
总耗时 = 基础运行时间 ×(1 + 失败率 × 重试代价)。失败率越高,重试拖慢整体越长,所以要两者一起优化。
  1. 单次运行时间 t一轮测试全部跑完的基准耗时。
    tt
  2. 失败率 r测试偶发失败的比例,r 越大意味着需要重试的用例越多。
    rr
  3. 重试代价 k每次重试额外消耗的时间成本,k 通常取正整数(如重试 2 次)。
    kk
  4. 总耗时 T基础时间加上重试带来的额外时间,得到整体预估。
    t×(1+r×k)t \times (1 + r \times k)

AI 应用场景

特征工程函数的回归测试

阶段:数据处理与特征工程

解决的问题:清洗、分箱、归一化函数一旦重构,行为变化很难被肉眼发现;在 notebook 里手跑只能验证当前单元格,无法形成历史回归保护。用 pytest 把每个特征函数写进 test_ 用例,fixture 提供固定样本 DataFrame,参数化覆盖 NaN、负值、超大值等边界情况。

具体案例:某推荐团队修改了用户活跃度归一化函数,测试套件马上发现深夜用户的高活跃度被从 0.9 压缩到 0.2,及时阻止了一次线上效果暴跌。

模型训练脚本的冒烟测试

阶段:模型训练与实验管理

解决的问题:训练脚本跑 10 小时后报 NaN loss,最后才发现是数据处理 bug——时间成本极高。在训练入口前加一层 pytest 冒烟测试:用 100 条样例验证 dataloader、损失函数、前向传播输出的 shape 和数值范围,跑完再正式起训。

具体案例:NLP 团队每次改动 tokenizer 后,CI 自动运行冒烟测试,3 分钟内发现序列长度越界,不必再等一晚训练回报错误。

推理服务接口的 mock 测试

阶段:模型部署与线上服务

解决的问题:推荐服务依赖下游召回接口,联调环境不稳定,导致自己的排序逻辑无法稳定验证。用 pytest-mock 把下游响应“打桩”,重点测试阈值分支、超时重试、兜底策略。

具体案例:广告排序服务通过 60 条 mock 用例覆盖了流量洪峰下的兜底逻辑,上线前跑通全部用例,线上事故率大幅下降。

Python 演示

python59 行
# -*- coding: utf-8 -*-"""PriceCalculator 的 pytest 测试套件, 演示 fixture 与参数化.""" import mathimport pytest  class PriceCalculator:    """根据商品单价和数量计算订单金额."""     def __init__(self, prices):        # prices: list of (price, quantity) tuples        self.prices = prices     def subtotal(self):        return sum(p * q for p, q in self.prices)     def apply_discount(self, rate):        # rate must be in (0, 1]        if not 0 < rate <= 1:            raise ValueError("discount rate must be in (0, 1]")        return self.subtotal() * rate  @pytest.fixturedef cart():    """构造一个包含苹果和香蕉的购物车."""    return PriceCalculator([(5.0, 2), (3.0, 4)])  @pytest.mark.parametrize("rate, expected", [    (1.0, 22.0),    (0.5, 11.0),    (0.25, 5.5),])def test_discount(cart, rate, expected):    assert cart.apply_discount(rate) == pytest.approx(expected)  def test_discount_invalid_rate(cart):    with pytest.raises(ValueError):        cart.apply_discount(0)    with pytest.raises(ValueError):        cart.apply_discount(1.5)  @pytest.mark.slowdef test_sqrt():    assert math.sqrt(16) == 4.0  @pytest.mark.skip(reason="online api not ready")def test_online_api():    # 模拟未来上线的接口, 先跳过    assert True  if __name__ == "__main__":    pytest.main([__file__, "-v", "-m", "not slow"])
目的:这个脚本是一个完整的 pytest 测试套件,演示了 fixture 注入、参数化、近似断言、异常断言、标记跳过和命令行执行入口。
  1. class PriceCalculator:被测业务类,模拟真实项目里的价格计算模块。
  2. @pytest.fixture把 cart 函数声明为 fixture,测试函数通过参数自动获取它。
  3. def cart():fixture 本体,返回一个预置数据的计算器对象,被多个测试共享。
  4. @pytest.mark.parametrize("rate, expected", [...])三组参数让 test_discount 变成三条独立用例。
  5. assert cart.apply_discount(rate) == pytest.approx(expected)浮点数比较必须用 approx 规避精度误差。
  6. with pytest.raises(ValueError):期望调用非法折扣率时抛出异常,异常也成为一种可验证的行为。
  7. @pytest.mark.skip(reason="online api not ready")给未上线的接口测试贴“跳过”便签,不阻塞其他用例。
  8. pytest.main([__file__, "-v", "-m", "not slow"])提供 Python 入口,直接运行本文件即可执行测试。

常见误区

误区我用了好多年 unittest,setUp 和 tearDown 已经很熟了,fixture 还要学习作用域和依赖注入,不如继续用老一套。

正解fixture 本质上是更简单的“工具包”。测试函数声明参数,框架自动注入;yield 负责清理;scope 控制生命周期。setUp 依赖继承,fixture 用装饰器声明一次、处处复用,代码量更少、意图更清晰。

为何常见:从 unittest 迁移时习惯了继承基类和固定方法名,对“依赖注入”这种新写法存在心理门槛。

误区我习惯在测试里 `print(x)`,然后盯着终端输出判断结果对不对,反正能看到数值。

正解断言的价值在于让框架自动判定结果并纳入报告。assert 失败时 pytest 会同时展示实际值和期望值,CI 里也能自动亮红灯;print 的输出在无人值守的流水线里根本没人看。

为何常见:写脚本时习惯了用 print 调试,没有意识到测试要的是“机器可读的判定结果”。

误区反正都是验证同一个功能,多写几个 assert 一次性检查完,省得写那么多函数。

正解一条用例的第一个 assert 失败后,后面的断言根本不会执行,报告只能看到第一个失败点。拆成多条用例并配合参数化,才能一次看到所有失败数据组合,定位效率成倍提升。

为何常见:觉得函数越长越省事,忽略了“失败定位粒度”这个关键指标。

误区参数化不就是语法糖吗?我写个 for 循环,循环里调用断言,效果一样。

正解for 循环遇到第一组失败就中断,后面的数据全部被跳过;参数化会把每组数据生成独立的用例记录,全部跑完,报告里逐条显示通过/失败/跳过。覆盖率口径也完全不同。

为何常见:没有意识到 for 循环的“短路”问题,误以为数据都一样被遍历了。

误区项目还小,把所有测试放在 `test_all.py` 一个文件里,跑起来多方便。

正解项目一长,收集变慢、定位困难、并行无法开启。按业务模块建 tests/ 子目录,每个模块一个测试文件,配 conftest.py 共享 fixture,可维护性大幅提升。

为何常见:早期脚本项目没有目录意识,功能膨胀后又舍不得花时间重构。

面试题

Q1. pytest 默认会收集哪些文件作为测试文件?

单选题 · easy

  • A.项目里所有 py 文件
  • B.test_ 开头的 py 文件← 正确答案
  • C.仅 tests 目录的文件
  • D.大小写不敏感的用例

答案:B。pytest 遵循测试文件命名约定 test_*.py 或 *_test.py,B 正确。A 全部收集会导致误跑工具脚本,C 少了约定的核心语义,D 与命名无关。

Q2. 浮点数断言推荐使用下列哪个 API?

单选题 · easy

  • A.assert a == b
  • B.assert a is b
  • C.assert a in b
  • D.pytest.approx← 正确答案

答案:D。浮点数存在二进制精度误差,0.1 + 0.2 == 0.3 可能为 False。pytest.approx 允许指定误差范围,是推荐的浮点比较方式。A 最容易被精度坑,B 比较对象身份,C 是成员判断。

Q3. fixture 默认的作用域是哪个?

单选题 · easy

  • A.全局 session
  • B.每个测试函数← 正确答案
  • C.每个测试类
  • D.每个测试模块

答案:B。fixture 默认 scope="function",即每个测试函数各自独立使用一次 fixture,互不干扰,隔离性好。session 是全流程共享,class/module 是更粗粒度的共享范围,均需显式声明。

Q4. 要把 3 组数据传给同一个测试函数,应使用哪个标记?

单选题 · mid

  • A.fixtured 标记
  • B.parametrize 标记← 正确答案
  • C.parameter 标记
  • D.paramstack 标记

答案:B。pytest 中用 @pytest.mark.parametrize 实现参数化,会为每组参数生成独立用例记录。A 是 fixture 相关的干扰项,C、D 在 pytest 中不存在。

Q5. 满足某个条件就跳过用例,应使用哪个装饰器?

单选题 · mid

  • A.@pytest.mark.skipif← 正确答案
  • B.@pytest.mark.skip
  • C.@pytest.mark.xfail
  • D.@pytest.mark.ignore

答案:A。skipif 在条件成立时跳过用例,例如平台判断;skip 是无条件跳过;xfail 表示预期失败;ignore 不是 pytest 内置标记。题干强调“满足条件”,故选 A。

Q6. 运行 pytest 时,哪些方式可以缩小测试范围?

多选题 · mid

  • A.指定文件路径← 正确答案
  • B.-k 按名称筛选← 正确答案
  • C.-m 按标记筛选← 正确答案
  • D.点击鼠标选择

答案:ABC。pytest tests/test_a.py 指定文件,-k "name" 按测试名称表达式过滤,-m "slow" 按标记过滤。D 不是命令行工具能力,pytest 没有鼠标选择功能。

Q7. conftest.py 的主要作用是什么?

单选题 · mid

  • A.只存放数据库配置
  • B.跨文件共享 fixture← 正确答案
  • C.必须放置测试数据
  • D.禁止在其中写代码

答案:B。conftest.py 是一个特殊的共享文件,其中的 fixture、钩子和插件配置可被同目录及子目录的所有测试文件自动共享,是工程级复用的核心机制。A、C、D 都是对它的错误限制。

Q8. 覆盖率报告中,“未命中行” 表示什么?

单选题 · hard

  • A.已执行但没报错
  • B.从未执行到某行代码← 正确答案
  • C.该行存在语法错误
  • D.该行是空行注释

答案:B。覆盖率工具逐行统计代码是否被执行。未命中行意味着测试根本没有走到这一行,风险较高。A 是已覆盖行,C 如果语法错误根本跑不起来,D 空注释行不参与统计。

Q9. 下列哪些属于 pytest 插件的典型能力?

多选题 · hard

  • A.生成 HTML 报告← 正确答案
  • B.并行执行用例← 正确答案
  • C.自动修复代码缺陷
  • D.模拟外部接口调用← 正确答案

答案:ABD。pytest-html 生成报告,pytest-xdist 并行执行,pytest-mock 模拟外部接口,这些都是真实插件能力。C 荒谬——插件负责检查和模拟,不负责自动改业务代码。

Q10. 关于参数化与 fixture 的交互,说法正确的有?

多选题 · extreme

  • A.每组参数会生成一条用例← 正确答案
  • B.用例中可同时使用 fixture← 正确答案
  • C.参数只能写字符串类型
  • D.参数值会展示在用例 id 中← 正确答案

答案:ABD。参数化每组数据生成独立用例记录;同一测试函数可同时接收 fixture 注入和参数化数据;ids 能自定义或自动用参数值生成用例名。C 错误——pytest 支持数字、列表、元组、字典等多种类型作为参数。