假如你是一家餐厅的主厨,最怕什么?不是某位客人不喜欢口味,而是你不知道哪一步出了问题。今天这道菜咸了,是盐坏了?是配菜不新鲜?还是颠勺时火候没拿捏好?为了快速锁定病灶,你需要一套“厨房质检流程”:备菜时先单独尝每一味调料,这是单元测试;食材下锅后把组合完整试味,这是集成测试;把各种检验手段按合适比例配成体系,这是测试金字塔;供应商迟到时让替身演员顶班,这是Mock与Stub;给质检记录表打勾、统计覆盖了多少食材,这是测试覆盖率;最后将所有质检动作按“备料—烹饪—试味”三段标准操作,这就是Arrange-Act-Assert。这套方法论,就是软件世界的“后厨质检学”——它让 Bug 不再是玄学,而是一次次可复现、可定位、可预防的确定性事件。

岗位关联

掌握测试与质量方法论,你写出的代码不再依赖“人肉调试”,而是靠自动化防线兜底。这是从“能跑”走向“可靠”的必修课,也是初级工程师迈向专业工程师的关键一步。

目录
  1. 可视化演示
  2. 单元测试基础
  3. 集成测试要点
  4. 测试金字塔
  5. Mock与Stub
  6. 测试覆盖率
  7. AAA组织规范
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

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

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

单元测试基础

备菜台是你的第一道防线。一块里脊肉不能等炒成菜才尝出馊味——切好之后,你会先单独切一小片,煎一煎、闻一闻,确认它本身没问题。在代码世界里,这个“单独检验一小片”的动作,就是单元测试。它不看配菜、不看火候,只看这一块食材自身是否合格。哪怕整个厨房乱成一锅粥,只要每份食材单独检验过关,最终出大问题的概率就会低得多。

单元测试的“单元”,通常指一个函数、一个类的方法,或一个独立组件。测试只关心它自己:给定输入,验证输出;不连接数据库,不发起网络请求,不依赖其他模块。正因为彻底隔离,单元测试运行极快——上千条用例往往几秒钟就跑完。这种“快”催生了持续集成里最珍贵的循环:改一行代码,几秒内就知道有没有弄坏别的东西。

常见的单元测试框架有 JUnit(Java)、pytest(Python)、Jest(JavaScript)。它们都提供断言(assert)、测试发现、覆盖率统计等基础能力。写单元测试时,核心是逼自己思考模块的“输入—输出”契约:函数接什么参数、返回什么结果、异常边界如何处理。如果一个函数连契约都讲不清楚,那它本身就该重构了。

这就像你在备菜台逐样检验食材:坏掉的胡椒罐被立刻挑出来丢掉,而不是混进菜里。单元测试的价值就在“早发现、快定位”——成本最低、收益最高。它解决的问题是“食材本身有没有坏”,而不是“整道菜好不好吃”。

P=npassntotal×100%\displaystyle P = \frac{n_{pass}}{n_{total}} \times 100\%
通过率是单元测试最朴素的健康指标,但它只回答“多少用例过了”,不回答“测得是否到位”。
  1. 分子统计通过数把测试中“通过”的用例数量收集起来,这是分子。
    npass=已通过测试数n_{pass} = \text{已通过测试数}
  2. 分母统计总数把该模块全部测试用例数量作为分母,包含通过和失败的。
    ntotal=测试总数n_{total} = \text{测试总数}
  3. 相除得比例用通过数除以总数,得到“通过的比例”,它介于0和1之间。
    npassntotal\frac{n_{pass}}{n_{total}}
  4. 化为百分数乘以100%,转成更直观的百分制,让人一眼看出质量水平。
    ×100%\times 100\%

集成测试要点

食材单独检验合格,不代表组合起来一定好吃。番茄单独很新鲜,牛肉单独很嫩,但火候不对、下锅顺序颠倒,出来的菜照样难以下咽。集成测试做的就是“组合后试味”:把多个模块拼在一起,验证它们协作时是否符合预期。它关注的是接口、数据传递和外部依赖的交互。

模块之间的“接口”是集成测试的头号对象。比如订单模块要调用库存模块的扣减接口,单元测试里两边各自通过,但如果订单模块传入的参数格式库存模块根本不认,那就要靠集成测试来暴露。这就是为什么集成测试往往需要真实的数据库、消息队列或第三方服务的测试环境,只有环境接近生产,协作问题才会显形。

集成测试的难点在于环境与数据配置:测试库要准备好、前置数据要造好、异步消息要等它落地。数据流走向验证是重头戏——一笔订单从下单到扣库存、到发通知、到生成账单,每一步的数据是否按预期流转。集成测试数量虽少,但每个用例覆盖的路径长、信息量大,跑起来也比单元测试慢得多。

这就像焖一锅红烧肉:肉要好、酱油要真,但更重要的是两者在锅里的相遇方式。集成测试保证的是“食材相遇不出乱子”——接口对得上、数据传得对、顺序不颠倒。它是单元测试和端到端测试之间的“承上启下”层。

集成测试=⋃i=1n验证(Mi, Mi+1)\displaystyle \text{集成测试} = \bigcup_{i=1}^{n} \text{验证}(M_i,\ M_{i+1})
这个公式想说:集成测试的本质是“把相邻模块的每一对协作关系都检验一遍”,用并集汇总所有交互点。
  1. 相邻模块$M_i$ 和 $M_{i+1}$ 表示系统中相邻的两个模块,比如订单模块和库存模块。
    Mi, Mi+1:相邻模块M_i,\ M_{i+1}:\text{相邻模块}
  2. 验证操作验证它们配合时的输入输出、接口契约是否符合预期。
    验证(Mi, Mi+1)\text{验证}(M_i,\ M_{i+1})
  3. 遍历并汇总从 $i=1$ 到 $n$ 遍历所有相邻组合,用并集把所有验证结果拼成完整结论。
    ⋃i=1n\bigcup_{i=1}^{n}
  4. 整体含义集成测试不是测某个模块,而是测“模块之间的关系网”。
    集成测试=⋃i=1n验证(Mi, Mi+1)\text{集成测试} = \bigcup_{i=1}^{n} \text{验证}(M_i,\ M_{i+1})

测试金字塔

聪明的厨师不会把大量时间花在每道菜上桌前搞满汉全席式的全流程试吃——那太慢太贵。他的质检体系是金字塔形的:底部是大量快速的单品检查,比如尝盐、闻油、按一按肉质的弹性;中部是中等数量的组合试味;塔尖才是极少数完整的宴会走查。这就是测试金字塔的直觉原型:底厚顶薄。

在软件世界里,金字塔给出了三种测试的理想配比:单元测试约占 70%,集成测试约占 20%,端到端测试约占 10%。单元测试便宜、快速、易定位;端到端测试最接近用户真实体验,但慢、脆、难排查——一旦失败,你往往不知道是前端、后端还是数据库哪一环出了问题。金字塔的启示是:用便宜的测试覆盖大多数逻辑,把昂贵测试留给关键链路。

比例并非死规矩,而是一盏警示灯。如果团队写了大量端到端测试,金字塔就会“倒置”:测试套件越来越慢,CI 流水线从几分钟拖到几十分钟,开发者开始为了赶进度随意跳过测试。倒置金字塔的团队,往往不是在测试质量上更优秀,而是在用昂贵的代价换取并不更高的安全感。

回到厨房,你是愿意花 50 分钟完整试吃一桌菜,还是花 10 分钟把每样食材都快速验过再加 20 分钟试吃几道关键菜?后者显然更高效。测试金字塔的本质是资源分配哲学:把黄金投在回报率最高的地方。

单元:集成:端到端≈7:2:1\displaystyle \text{单元} : \text{集成} : \text{端到端} \approx 7 : 2 : 1
7:2:1 是经验法则,提醒你把资源投在“性价比最高”的楼层,而不是把宝全押在塔尖。
  1. 三者的比率单元、集成、端到端三种测试的数量比约为 7:2:1。
    单元:集成:端到端\text{单元} : \text{集成} : \text{端到端}
  2. 数量递减越往上越少,因为越往上执行越慢、维护越贵。
    ≈7:2:1\approx 7 : 2 : 1
  3. 取近似而非精确不同项目可微调,但“底部大”的原则不变。
    ≈\approx

Mock与Stub

餐厅后厨里,供应商送来的鱼偶尔会晚点。为了不耽误备菜,主厨会安排一个帮厨“替身”先顶上——替身的职责是让流程先跑起来。在测试世界里,当我们不想真的连接数据库、调用支付接口时,也会请“替身”上场。这些替身统称测试替身,其中最常用的两个就是 Stub 和 Mock。

两者的差别可以用一个镜头来记:Stub 像是“举着标准菜牌的替身”——你说“上菜”,它就把预设好的菜递给你,你验证的是“拿到的东西对不对”,这是状态验证;Mock 则像是“带着小本本的替身”——它不光递菜,还会记录你喊了它几次、喊的时候说了什么话,之后你要检查“它是不是被正确调用了”,这是行为验证。

实际编码中的选用很简单:如果你只关心“调用后返回值是否正确”,用 Stub 即可;如果你还关心“这个依赖是否被调用了、调用了几次、参数对不对”,就必须用 Mock。很多框架(如 Mockito、unittest.mock)同时支持两种用法——同一个替身,你只用它的“预设数据”功能时它是 Stub,你用它的“调用断言”功能时它就变成了 Mock。

这与厨房如出一辙:如果只是需要一条“不会坏的鱼”来走流程,让帮厨举着菜牌就行(Stub);如果你要确认“服务员有没有真的向供应商下单、下了几次”,就得让帮厨记录每一次传话(Mock)。替身的价值是让后厨运转不被外部世界的不确定性卡住。

Stub→状态验证,Mock→行为验证\displaystyle \text{Stub} \rightarrow \text{状态验证} \quad,\quad \text{Mock} \rightarrow \text{行为验证}
一条公式记住核心差异:Stub 回答“结果对不对”,Mock 回答“过程对不对”。
  1. Stub 指向状态Stub 返回预设数据,测试只断言“返回值是否符合预期”。
    Stub→状态验证\text{Stub} \rightarrow \text{状态验证}
  2. Mock 指向行为Mock 记录调用过程,测试断言“是否被调用、调用参数、调用次数”。
    Mock→行为验证\text{Mock} \rightarrow \text{行为验证}
  3. 两者配合隔离外部依赖时,按验证目标选择替身类型,而不是盲目 All-in。
    →按需选用\rightarrow \text{按需选用}

测试覆盖率

质检员每天会在记录表上打勾:今天检查了 70 种食材,占总食材的 70%。这个数字看起来令人安心,但注意——它只说明“这些食材被看过了”,并不说明“这些食材都是合格的”。软件世界的测试覆盖率(Coverage)同理:它度量的是“测试执行到了多少行代码”,而不是“这段代码里没有 Bug”。

覆盖率最基础的是行覆盖率,即测试执行过的代码行占总代码行的比例。更严格的是分支覆盖率,它统计 if/else、switch 等分支是否都被走到。分支覆盖比行覆盖更接近“逻辑验证”——同样是 10 行代码,可能有的分支永远没被触发,行覆盖却显示 100%。

覆盖率的局限必须认清:一个模块覆盖率 100%,可能只是所有代码行都被“路过”,却没有任何一行被“验证”了正确的行为。比如一个函数内部 if (a > b) 的两条分支都执行了,如果断言写得软弱无力,照样会有 Bug 溜走。所以覆盖率是“体检表”,不是“免死金牌”。它最合理的用法是配合缺陷密度——如果在覆盖率高的模块里 Bug 依旧频发,说明测试质量差,该回头检查断言的有效性了。

质检记录表上打满勾,不代表今晚的宴席一定完美;但一张很少打勾的表,肯定说明品控形同虚设。覆盖率的正确姿势是“把它当温度计,而不是体检报告”——发现低覆盖率区域就去补测,但永远不要为了凑数字而写空断言。

Cline=LexecutedLtotal×100%\displaystyle C_{line} = \frac{L_{executed}}{L_{total}} \times 100\%
行覆盖率公式:执行过的行数除以总行数。它度量“走到过多少行”,不度量“验得对不对”。
  1. 执行行数测试运行过程中真实执行到的代码行数。
    Lexecuted=执行行数L_{executed} = \text{执行行数}
  2. 总行数被测模块的全部代码行数。
    Ltotal=总行数L_{total} = \text{总行数}
  3. 比值执行行数除以总行数,得到覆盖率小数。
    LexecutedLtotal\frac{L_{executed}}{L_{total}}
  4. 百分数乘以 100% 转为覆盖率百分比,用于和团队目标对比。
    ×100%\times 100\%

AAA组织规范

后厨最怕混乱的操作台:盐罐找不着、锅铲插在炒锅里、配菜和剩菜混在一起。为此,成熟的厨房会把每个烹饪动作拆成三段:备料——把食材、调料、厨具全部备好;烹饪——开火执行;试味装盘——尝一口、确认味道、端上桌。这套三段式在测试代码里就是 Arrange-Act-Assert,它让每个测试用例的结构都清晰可读。

Arrange(准备)是测试的“备料阶段”:创建被测对象、初始化依赖、设置 Mock 的预期返回值、准备输入数据。这一段是测试的“场景搭建”,决定了测试的故事背景。Act(执行)是“烹饪阶段”:只做一件事——调用被测的方法或函数。这一段通常只有一行代码,如果有超过三行的“执行”动作,说明被测单元的接口设计可能有问题。Assert(断言)是“试味阶段”:验证执行结果是否符合预期,可以是返回值、异常、或者 Mock 的调用行为。

AAA 规范最大的价值是可读性维护。其他工程师在读测试时,先看 Arrange 了解背景,再看 Act 抓住动作,最后看 Assert 明白验收标准——整个测试读起来像一段微型故事的“起因—经过—结果”。反之,如果三者混成一团,测试一挂,你往往要花十分钟才搞明白它在测什么。

操作台的标准三段式,让任何一个帮厨都能快速接手后厨工作;同样,AAA 规范让任何一个新成员都能快速读懂测试意图。这不是风格偏好,而是团队协作的效率基础设施——可读的测试代码,就是可维护的质量资产。

Test=Arrange⊕Act⊕Assert\displaystyle \text{Test} = \text{Arrange} \oplus \text{Act} \oplus \text{Assert}
一条公式记住测试代码的骨架:一个完整测试 = 准备 ⊕ 执行 ⊕ 断言,三个阶段缺一不可。
  1. 准备阶段构造被测对象、装配依赖与预设数据。
    Arrange\text{Arrange}
  2. 执行阶段调用被测对象的核心方法,触发行为。
    Act\text{Act}
  3. 断言阶段用断言语句验证结果与预期一致。
    Assert\text{Assert}
  4. 串联符号$\oplus$ 表示三段按顺序串成一条完整流水线,顺序不可颠倒。
    ⊕串联\oplus \text{串联}

AI 应用场景

场景一:CI/CD 流水线中的自动化质量门禁

阶段:模型/服务上线前的持续集成与持续交付

解决的问题:AI 模型推理服务上线前,传统做法是“人肉部署 + 手工冒烟”,效率低且容易漏回归。利用测试金字塔思想,在 CI 流水线中配置三层测试:底层跑大量快速的单元测试(数据处理函数、特征工程每个纯函数),中层跑集成测试(模型服务与特征库、向量数据库的真实交互),顶层只跑少量端到端回归(完整推理链路)。金字塔配比让流水线在“快”和“稳”之间取得平衡。

具体案例:某推荐系统团队将特征拼接逻辑的单元测试从 30 个扩充到 300 个,同时把端到端测试从 50 个压到 8 个。流水线耗时从 45 分钟降到 12 分钟,线上回归事故减少 60%。质量门禁让每个 PR 合入前自动执行三层测试,不通过不允许合并。

场景二:微服务联调中的 Mock 隔离

阶段:多个 AI 服务并行开发时的接口联调

解决的问题:大模型调用服务、向量检索服务、用户画像服务通常由不同小组并行开发,互相依赖的接口常因对方未就绪而阻塞联调。通过 Mock 替身,开发者在本地为每个下游服务建立“替身”:用 Stub 返回预设的向量结果,用 Mock 校验自己的服务是否以正确参数调用了下游。等真实服务就绪后,再切换为集成测试验证真实交互。

具体案例:某 LLM 应用团队在开发“检索增强生成”链路时,向量检索服务尚在重构。前端服务先用 Mock 固定返回 Top5 文档,使 RAG 管线调试不被阻塞;下游就绪后,通过集成测试一次性验证了真实检索与生成模型的协作无误。

场景三:覆盖率报告驱动测试补充

阶段:AI 代码质量评估与测试用例补强

解决的问题:AI 模型训练的数据预处理代码通常分支极多(缺失值、异常值、边界值),极易漏测。通过行覆盖率与分支覆盖率报告,定位未被覆盖的边界分支(比如空列表、超大数值、NaN),有针对性地补充测试数据。同时结合缺陷密度,识别“覆盖率高但缺陷也多”的可疑模块,回头检查断言质量。

具体案例:某风控模型团队发现特征工程模块覆盖率 92% 但线上频繁出 NaN 异常,排查发现高覆盖率下的关键分支“空值兜底”从未被真正断言。补上该分支的测试后,相关线上事故归零。

Python 演示

python39 行
import unittestfrom unittest.mock import Mock  def get_discount(user_client, user_id):    # 根据用户信息返回折扣,vip 打 8 折,普通用户不打折    user = user_client.fetch(user_id)    return 0.8 if user["vip"] else 1.0  class TestDiscount(unittest.TestCase):    """演示 Arrange-Act-Assert 结构与 Mock/Stub 的用法"""     def test_vip_discount_with_mock(self):        # Arrange: 准备替身,预设返回数据        mock_client = Mock()        mock_client.fetch.return_value = {"vip": True}         # Act: 执行被测函数        discount = get_discount(mock_client, 1)         # Assert: 断言结果,并验证 mock 行为        self.assertEqual(discount, 0.8)        mock_client.fetch.assert_called_once_with(1)     def test_normal_user_with_stub(self):        # Arrange: 用 stub 返回普通用户数据        stub_client = Mock()        stub_client.fetch.return_value = {"vip": False}         # Act: 执行被测函数        discount = get_discount(stub_client, 2)         # Assert: 只验证状态,不验证调用过程        self.assertEqual(discount, 1.0)  if __name__ == "__main__":    unittest.main(verbosity=2)
目的:用 Python 标准库 unittest 演示了单元测试的 AAA 三段式结构,以及 Mock(验证调用行为)与 Stub(返回预设数据)的实际差异。
  1. mock_client = Mock()创建测试替身,隔离 user_client 这个外部依赖,避免连接真实用户服务。
  2. mock_client.fetch.return_value = {"vip": True}Stub 的核心用法:预设返回值,让替身“按剧本说话”。
  3. discount = get_discount(mock_client, 1)Act 阶段:只调用一次被测函数,保持执行动作单一清晰。
  4. self.assertEqual(discount, 0.8)状态验证:断言返回值是否正确。
  5. mock_client.fetch.assert_called_once_with(1)Mock 独有的行为验证:确认 fetch 被调用了恰好一次,且参数是 1。
  6. unittest.main(verbosity=2)启动测试运行器,verbosity=2 让每个用例的通过/失败信息更详细。

常见误区

误区“我们模块的测试覆盖率已经冲到 100% 了,质量绝对有保障,可以放心上线。” 我经常在代码评审里看到这种迷之自信。

正解覆盖率只说明“代码行被路过”,不说明“代码行为被验证”。就像质检员在每袋大米上打了个勾,但没拆开包装闻一闻——打勾流程完整,大米照样可能发霉。真正重要的是断言质量:覆盖率 100% 但断言全是 assertTrue(True) 的测试套件,比 60% 覆盖率但断言扎实的套件更危险。覆盖率要与缺陷密度结合看:高覆盖率 + 高缺陷密度,说明测试在“假装努力”。

为何常见:因为覆盖率是一个“看得见、可比较、容易汇报”的数字,团队容易把数字当目标本身,而忽略了数字背后的验证力度。

误区“把所有外部依赖都 Mock 掉,测试才够独立!数据库、缓存、消息队列,统统用替身。” 结果测试跑得飞快,上线却连环炸。

正解Mock 是“隔离坏区”的工具,而不是“逃避真实”的捷径。用量过大时,测试验证的是“替身与代码的剧本配合”,而不是“真实代码与真实依赖的兼容性”。集成测试的价值恰恰在于用真实的数据库和网络发现 Mock 永远无法暴露的问题——比如字段名拼错、超时行为、序列化格式不兼容。正确姿势是金字塔配比:多数单元测试用 Mock 隔离,少数集成测试必须走真实依赖,用成本换真实感。

为何常见:因为 Mock 让测试变得又稳又快,初写测试的人容易沉迷这种“确定性”,忘记了测试的最终目标是保障真实系统的可靠性。

误区“我的单元测试里直接连了测试库,顺便验一下 SQL 语句,反正数据是假的。” 这种测试跑一次要几秒,还经常因为库没启动而失败。

正解一旦测试触碰了数据库、文件系统或网络端口,它就从“单元测试”变成了“集成测试”,必须按集成测试的标准来管理:准备独立的测试环境、处理数据清理、接受较慢的运行速度。单元测试要的是纯内存、纯函数、毫秒级反馈。想验证 SQL?请把那条用例挪到集成测试层,让它名正言顺。

为何常见:因为写测试时“能跑通”的诱惑太大,直接连库最省事;加上很多人分不清单元测试与集成测试的界线,用同一套风格写了所有测试。

误区“测试只要不报错就算过了!” 于是断言全是 `assertIsNotNone(x)` 或者干脆不写断言,只调用函数确保不抛异常。

正解断言是测试的灵魂,没有断言的测试只是“代码执行器”。就像试味时只瞥了一眼菜品,没真正尝一口就端上桌。有效断言的标志是“能抓住回归”:如果故意改坏被测逻辑,测试必须失败。建议在写完用例后做个“变异测试”小实验——手动改一个返回值的符号,看看测试会不会红。如果不红,说明断言没抓住要害。

为何常见:因为写断言要求对被测模块的行为有精确理解,偷懒的开发者用“不报错”来糊弄自己,实际上把测试写成了噪音。

面试题

Q1. 单元测试的主要验证对象是什么?

单选题 · easy

  • A.多个模块的协作
  • B.单个模块的独立行为← 正确答案
  • C.整个系统的端到端流程
  • D.界面上的按钮交互

答案:B。单元测试聚焦“一个单元”(函数/方法/组件)的独立行为,不依赖外部模块。如同备菜台单独检验一块里脊肉有没有馊味;而多模块协作属于集成测试范畴,端到端流程属于更高层的测试。

Q2. 测试金字塔中,数量占比最大的应该是哪一层?

单选题 · easy

  • A.单元测试← 正确答案
  • B.集成测试
  • C.端到端测试
  • D.手工测试

答案:A。金字塔底厚顶薄,单元测试数量最多(约70%),因为它快、便宜、易定位。集成测试居中(约20%),端到端最少(约10%)。如同厨房里大批量快速的单品检查远比昂贵的全流程试吃多得多。

Q3. Stub 与 Mock 的主要区别是什么?

单选题 · easy

  • A.Stub 验证行为,Mock 返回预设数据
  • B.Stub 返回预设数据,Mock 验证行为← 正确答案
  • C.两者完全相同,只是名字不同
  • D.Stub 只用于集成测试,Mock 只用于单元测试

答案:B。Stub 像举着菜牌的替身——返回固定数据,验证“状态”;Mock 像带小本本的替身——记录调用次数和参数,验证“行为”。前者回答“结果对不对”,后者回答“过程对不对”。

Q4. 集成测试通常关注哪些方面?

多选题 · mid

  • A.模块间接口契约← 正确答案
  • B.外部依赖的真实交互← 正确答案
  • C.单个函数的语法正确性
  • D.数据流在各模块间的走向← 正确答案

答案:ABD。集成测试验证“食材组合后好不好吃”——接口契约(选项A)、外部依赖交互(选项B)、数据流走向(选项D)都是核心对象。单个函数的语法正确性是单元测试的职责,不属于集成测试范畴。

Q5. 某模块的行覆盖率为 70%,最准确的理解是?

单选题 · mid

  • A.该模块 70% 的代码没有 Bug
  • B.该模块 70% 的代码行被测试执行到← 正确答案
  • C.该模块通过了 70% 的测试用例
  • D.该模块性能达到正常的 70%

答案:B。行覆盖率只度量“测试执行到的代码行比例”,相当于质检表上打了多少勾。它不代表没有 Bug,也不代表测试用例通过率,更与性能无关。70% 覆盖率可能仍藏着大量未被验证的边界行为。

Q6. 以下哪些做法属于对 Mock 的正确使用?

多选题 · mid

  • A.预设返回值以隔离外部依赖← 正确答案
  • B.断言外部依赖的调用次数← 正确答案
  • C.启动真实数据库以验证 SQL
  • D.断言外部依赖收到的调用参数← 正确答案

答案:ABD。Mock 的两大能力是预设返回值(选项A)和行为验证(选项B、D)。启动真实数据库属于集成测试的手段,与 Mock 隔离的初衷相反。Mock 的价值是让外部依赖“可控制、可观察”。

Q7. 测试金字塔中"7:2:1"的比例具体指什么?

单选题 · mid

  • A.单元:集成:端到端测试的数量比← 正确答案
  • B.代码行数比
  • C.测试人员与开发人员的配比
  • D.开发:测试:运维的时间比

答案:A。7:2:1 是三种自动化测试的数量经验比例:单元最多、集成中等、端到端最少。它体现资源分配哲学——把预算投在性价比最高、反馈最快的测试层。它不涉及人员配比或代码行数。

Q8. 在 AAA 结构中,下列哪个动作属于 Arrange(准备)阶段?

单选题 · hard

  • A.调用被测函数并传入参数
  • B.构造被测对象并设置 Mock 返回值← 正确答案
  • C.用 assert 比较实际值与期望值
  • D.运行 pytest 命令执行测试

答案:B。Arrange 是“备料”——构造对象、装配依赖、预设 Mock 数据,为测试搭好舞台。调用被测函数是 Act(执行),断言是 Assert,运行测试命令不属于 AAA 三阶段之一。

Q9. 以下哪些是过度使用 Mock 的典型危害?

多选题 · hard

  • A.测试与真实行为严重脱节← 正确答案
  • B.Mock 维护成本随依赖数量上升← 正确答案
  • C.测试运行速度显著变慢
  • D.真实依赖的兼容性问题被掩盖← 正确答案

答案:ABD。Mock 过多导致测试验证的是“替身剧本”而非真实行为(选项A),维护成本升高(选项B),真实依赖的接口变化被掩盖(选项D)。Mock 通常让测试变快而非变慢,因此选项C错误。

Q10. 某模块覆盖率高达 90%,但缺陷密度却是全项目最高的 3 倍。最合理的判断是什么?

单选题 · extreme

  • A.覆盖率已达标,缺陷是历史遗留,不必处理
  • B.模块测试存在“高覆盖低质量”问题,需审视断言有效性← 正确答案
  • C.覆盖率超过 80%,按规矩可以停止补测
  • D.缺陷密度与覆盖率无关,应忽略此信号

答案:B。高覆盖率 + 高缺陷密度是典型的“虚假安全感”信号:代码行被路过了,但断言软弱或关键分支未验证。如同质检表打满勾却接连有顾客投诉——问题不在“检查数量”,而在“检查深度”。正确做法是审计该模块的断言质量并补强关键分支测试。