假设你是《订单快递》这部戏的导演:主角要演一场“给顾客发发货短信”的戏,可你不能真的让演员去邮局跑一趟——太慢、太依赖天气、还可能真的把信寄出去。于是你请来一个替身演员,长得像、台词熟,站在台上配合主角把戏演完。这个替身演员,就是单元测试里的测试替身(Test Double)。 替身也分两种:一种是“道具型替身”,导演喊“报单号”,他就机械地回答“10086“,从头到尾不需要真本事,这叫 Stub(桩);另一种是”记账型替身“,他不仅对台词,还会在小本子上记下”某时某刻导演让我说了哪句话、顺着什么手势递了快递单”,散场后导演翻本子来确认主角有没有按剧本走位,这叫 Mock(模拟对象)。 Stub 和 Mock 看起来都是“找人代替真身”,但一个只负责提供预设答案,一个还要记录并验证交互过程。弄懂这层区别,你写单元测试时就知道:什么时候该找个桩站在那儿,什么时候该安个摄像头盯着对手戏。

岗位关联

对 AI 从业者来说,模型推理、数据管线、外部服务调用都依赖接口协作,用对 Stub 和 Mock 能让你写出稳定不脆的单元测试,保证 AI 工程交付质量。

目录
  1. 可视化演示
  2. 概念与定位
  3. 核心区别
  4. 适用场景
  5. 编写实践
  6. 最佳实践
  7. 家族对比
  8. 常见陷阱
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

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

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

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

概念与定位

片场里有两种替身,你在监视器前一眼就能分清:Stub 是道具型替身,导演问什么台词,他就照本宣科回答预设好的那一句,多一个字都没有;Mock 是记账型替身,他答完台词还会低头在小本子上写两笔,记下“刚才谁给我递了眼神、我回了几句话、顺序如何”。这两种替身,共同构成单元测试里最常用的测试替身阵容。

在单元测试语境里,“被测系统”就是舞台上的主角,外部依赖(数据库、邮件服务、AI 模型接口)是需要被替代的配角。当你只想让配角“站在那里配合演出”时,用 Stub 就够了;当你担心主角“有没有真的按照剧本做动作”时,需要的是 Mock。理解这一层,是进入测试工程的大门钥匙。

给替身安排岗位时,记住 Stub 是“工具人”,Mock 是“见证人”。你不需要让见证人背台词,也不该让工具人记笔记,选对角色,戏才不塌。

测试替身⊇{Stub, Mock, Fake, Dummy, Spy}\mathrm{测试替身} \supseteq \{\mathrm{Stub},\ \mathrm{Mock},\ \mathrm{Fake},\ \mathrm{Dummy},\ \mathrm{Spy}\}
Stub 和 Mock 只是“测试替身”家族里的两位成员,它们共享“替代真实依赖”的身份,但性格完全不同。
  1. 测试替身这是一个容器概念,代表所有在测试中替代真实对象的程序片段。
    测试替身\mathrm{测试替身}
  2. Stub特性是“输入固定、输出预设”,像一台自动售货机——按键必出货,且货永远相同。
    Stub:入参↦预设值\mathrm{Stub}: \mathrm{入参} \mapsto \mathrm{预设值}
  3. Mock特性是“先记录、后判决”,像带录音笔的演员,既表演也留证据。
    Mock:(入参, 被调用信息)↦记录\mathrm{Mock}: (\mathrm{入参},\ \mathrm{被调用信息}) \mapsto \mathrm{记录}

核心区别

散场之后,两种导演的工作方式不同。只关心结果的导演,把 Stub 返回的单据拿过来,看一眼金额对不对就收工回家——这叫状态验证;较真的导演则翻开 Mock 的小本子,核对“主角是什么时候递的单据、递给了谁、说了哪句台词”——这叫行为验证。

对应到代码上,状态验证的断言对象是返回值:被测模块调用 Stub 拿到结果,再与预期值比对。行为验证的断言对象是调用过程:Mock 记录下“被调用了哪些方法、传入了哪些参数、调用了多少次”,测试通过查询这些记录来确认行为符合预期。

举个直白的例子:测一个订单服务,Stub 方式断言“返回的订单状态是已发货”,Mock 方式断言“send 方法被调用过一次,参数收件人是 alice@example.com”。前者测商家的结算逻辑,后者测流程的传递动作,各管一段。

状态验证: 断言(返回值=预期值);行为验证: 断言(记录∋预期调用)\text{状态验证:}\ \mathrm{断言}(\mathrm{返回值} = \mathrm{预期值});\quad \text{行为验证:}\ \mathrm{断言}(\mathrm{记录} \ni \mathrm{预期调用})
前者问“结果对吗”,后者问“方法对吗”——这是二者最本质的分水岭。
  1. 返回值与预期值状态验证把 Stub 返回的数据当作“证据”,只比对数值或对象状态。
    断言(返回值=预期值)\mathrm{断言}(\mathrm{返回值} = \mathrm{预期值})
  2. 记录与预期调用行为验证把 Mock 的调用记录当作“证据”,检查列表中是否包含预期的那次调用。
    断言(记录∋预期调用)\mathrm{断言}(\mathrm{记录} \ni \mathrm{预期调用})
  3. 等号与包含号等号表示“结果精确匹配”,包含号表示“该调用必须发生过”,两种符号恰好对应两种验证哲学。
    =vs∋= \quad \mathrm{vs} \quad \ni

适用场景

现在你坐在导演椅上,手里有两份剧本。第一份剧本里,主角需要知道“今天的快递费价格表”才能算运费——这种事只需要一个人站在那儿报个数字,Stub 出场;第二份剧本里,主角负责“呼叫仓库发货”,你必须确认他真的呼叫了仓库而不是自己把货搬走了——这种交互必须留痕,Mock 上场。

工程上有个简单判断法:如果测试失败只是因为返回值不对,用 Stub;如果测试失败是因为依赖没有被正确调用(顺序、次数、参数不对),用 Mock。读取配置、查数据库、调用计算服务,通常 Stub 足够;发消息、写日志、调第三方 API,通常需要 Mock。

如果你发现测试里塞满了 Mock 才过得去,先别急着写替身,问问设计是否合理。好的单元测试跟好导演一样,能删的戏份绝不硬加。

需要验证: 返回结果 → Stub,调用过程 → Mock\mathrm{需要验证:}\ \mathrm{返回结果}\ \rightarrow\ \mathrm{Stub},\quad \mathrm{调用过程}\ \rightarrow\ \mathrm{Mock}
两个箭头都是“优先”而非“绝对”。真实依赖能跑通就别用替身;替身是为了隔离,不是为了偷懒。
  1. 返回结果方向被测代码把外部依赖当“数据源”时,注入 Stub 提供稳定数据即可。
    返回结果 → Stub\mathrm{返回结果}\ \rightarrow\ \mathrm{Stub}
  2. 调用过程方向被测代码把外部依赖当“动作执行者”时,必须用 Mock 记录动作是否发生。
    调用过程 → Mock\mathrm{调用过程}\ \rightarrow\ \mathrm{Mock}
  3. 方向箭头箭头表示“考察的重点决定选择”,而不是“某个库或框架强制绑定”。

编写实践

到了实操环节,给替身安排什么装备?Python 里自带 unittest.mock 库,一行 mock.Mock() 就能制造一个多功能替身:用 .return_value 设定 Stub 的预设答案,用 .assert_called_once_with(...) 做 Mock 的行为验证——同一个对象,两种用法,上得厅堂下得厨房。Java 阵营响当当的 Mockito 同理:when(mock.price()).thenReturn(80) 是 Stub 手感,verify(mock).send(email) 是 Mock 手感。

写的时候抓住三条要领:第一,替身只替外部边界,不替内部值对象;第二,Stub 配置写“当碰到什么入参、返回什么结果”,Mock 断言写“期望这个方法被带这些参数调用 N 次”;第三,测试结尾先查行为再查状态,顺序别乱。

在 Python 里,mock.Mock() 对象既能当 Stub 也能当 Mock,这常让新手误以为二者没有区别。其实区别不在工具而在断言动作:你不去查调用记录,它就是个 Stub 的命;你调用 assert_called_*,它就觉醒成 Mock 了。

替身.预设值 ⇒ 状态准备;verify(替身) ⇒ 行为验收\mathrm{替身.预设值} \ \Rightarrow\ \text{状态准备};\quad \mathrm{verify}(\mathrm{替身}) \ \Rightarrow\ \text{行为验收}
preset 是 Stub 的“喂饭”动作,verify 是 Mock 的“翻账本”动作。
  1. 替身。预设值这是 Stub 的配置语句,相当于设定自动售货机的出货内容。
    替身.预设值\mathrm{替身.预设值}
  2. 状态准备预设好返回值后,被测代码只需正常调用,Stub 便返回设定数据。
    ⇒ 状态准备\Rightarrow\ \text{状态准备}
  3. verify(替身)这是 Mock 的验收语句,相当于查看替身的小本子。
    verify(替身)\mathrm{verify}(\mathrm{替身})
  4. 行为验收验证“某方法被调用、参数符合、次数符合”,通过则测试通过。

最佳实践

一个成熟的导演,选替身有几条铁律。第一,替身数量最小化——能用真实组件的地方绝不引入替身,替身只出现在测试与被测代码的真实边界上;第二,命名清楚——stub_price、mock_email_sender 一眼能看出身份,别用 mock1、fake2 糊弄人;第三,先考虑真实依赖——内存数据库、本地测试容器能用就先用,替身是最后一道屏障。

把“测试替身”想象成剧场里的消防通道:修太多反而堵了主路。过度 Mock 会让单元测试变成“自说自话”——测试验证的是 Mock 的行为是否符合 Mock 的配置,而不是真实系统的行为。越少的替身意味着越少的维护负担。

写测试时问自己一句:删掉这个替身,测试会崩吗?如果不会崩,请删。留下必要的最小替身阵容,测试会跑得更快、更稳、更可信。

维护成本 ∝替身数量2\text{维护成本 } \propto \text{替身数量}^2
这不是精确公式,而是经验法则:每多一个替身,你就要多维护一份“剧本”,数量越多,成本增幅越大。
  1. 维护成本包括替身配置的修改、Mock 断言随接口变化而调整的工作量。
    维护成本\text{维护成本}
  2. 替身数量测试中引入的 Stub / Mock / Fake 数量总和。
    ∝替身数量2\propto \text{替身数量}^2
  3. 平方关系替身之间还存在隐性联动,新增一个替身可能逼着调整另一个替身的参数,成本呈非线性上涨。

家族对比

把替身剧场转一圈,你会发现还有几位“编外”成员。Dummy 是背景板演员,只是来凑个人数让构造函数不报错,台词和戏份都不参与;Fake 是“缩略版剧组”,比如用内存版数据库替代真实数据库,它内部有真实业务逻辑但运行得很快;Spy 是“带录音功能的 Stub”,在真身外面包一层记录调用的壳;而 Stub、Mock 两位正是这串能力阶梯上的核心。

  • Dummy(哑元):占位用,无逻辑,比如只传空对象不让构造器崩溃。
  • Fake(假实现):简化版真逻辑,比如内存数据库、静态字典模拟表。
  • Stub(桩):预设返回,管“结果对不对”。
  • Spy(间谍):真实对象 + 调用记录,半 Stub 半 Mock。
  • Mock(模拟):记录调用 + 断言行为,管“过程对不对”。

选替身时,先问自己“我需要哪个档次的能力”,而不是“哪个听起来高级”。Dummy 和 Fake 是 Stub 与 Mock 的邻居,它们一起撑起了测试隔离的地基。

Dummy≺Stub≺Spy≺Mock(验证能力递增)\mathrm{Dummy} \prec \mathrm{Stub} \prec \mathrm{Spy} \prec \mathrm{Mock} \quad (\text{验证能力递增})
“从 Dummy 到 Mock”是一条替身“手段越来越多”的光谱:越往右,对交互的验证能力越强,但配置复杂度也越高。
  1. Dummy(哑元)只解决“参数不能为空”的问题,不产生任何真实行为。
    Dummy\mathrm{Dummy}
  2. Stub(桩)在 Dummy 基础上增加“返回预设值”的能力。
    Dummy≺Stub\mathrm{Dummy} \prec \mathrm{Stub}
  3. Spy(间谍)在 Stub 基础上增加“记录真实调用”的能力。
    Stub≺Spy\mathrm{Stub} \prec \mathrm{Spy}
  4. Mock(模拟)在 Spy 基础上进一步把“断言调用记录”作为内置能力。
    Spy≺Mock\mathrm{Spy} \prec \mathrm{Mock}

常见陷阱

片场最容易翻车的,是导演自己把道具和账本搞混。第一大事故:把 Mock 当 Stub 用——测试只关心返回值,却给 Mock 加了一堆 verify 断言,结果接口内部稍微改个参数名,测试就哗啦啦全红,修都修不完。第二大事故:过度 Mock——每个类都配一个 Mock,测试跑得慢、改起来累。第三大事故:行为耦合——连“临时变量怎么组合”都去断言,把 Mock 当成了代码显微镜。

深挖下去,问题根子在于:把“实现细节”错当“行为契约”去验证。行为验证应该只针对“对外部世界产生影响的调用”(比如发了邮件、写了消息),而不是针对内部计算步骤。Stub 与 Mock 的正确用法,是三分靠配置、七分靠克制。

记住这个画面:替身是舞台边缘的钢管,扶一把保持平衡就好,别让演员全场抱着钢管跳舞。Stub 与 Mock 用得好,是测试的加速器;用乱套,就是测试的定时炸弹。

替换难度 ∝被 Mock 接口数⇒尽量用替身→仅限真实边界\text{替换难度 } \propto \text{被 Mock 接口数} \quad \Rightarrow\quad \text{尽量用替身} \rightarrow \text{仅限真实边界}
替身应该在“必须隔离的环境外设”处使用,而不是越过边界接管内部逻辑。
  1. 替换难度指后续重构时,修改测试替身所付出的成本。
    替换难度\text{替换难度}
  2. 被 Mock 接口数测试中为外部依赖创建的替身数量。
    ∝被 Mock 接口数\propto \text{被 Mock 接口数}
  3. 真实边界结论替身只应出现在“系统与外部世界”的接触点上,即 I/O 边界附近。
    ⇒ 仅限真实边界\Rightarrow\ \text{仅限真实边界}

AI 应用场景

场景一:AI 模型调用链路的单元测试

阶段:模型服务集成与测试

解决的问题:在 NLP 服务里,业务代码会调用大模型 API 完成意图识别。真实调用慢、贵、且结果随机,无法在 CI 中做稳定断言。传统做法是直接连真实 API,跑一次测试花费数秒并产生费用;现在用 Mock 替代模型客户端,验证“prompt 是否拼装正确、请求超时参数是否设置到位”。

具体案例:某客服机器人项目,测试用 mock.Mock() 替换 LLMClient,设置 .chat.return_value = "退款",随后断言 chat 被调用一次且传入的 system_prompt 包含用户画像信息。这样既验证了取回意图的分类逻辑(Stub 职责),又验证了 prompt 确实被发出(Mock 职责)。

场景二:推荐系统特征管线的隔离测试

阶段:特征工程与数据管线测试

解决的问题:推荐服务依赖特征存储 SDK 拉取用户画像,一旦特征服务抖动,整条管线测试全挂。用 Fake 内存特征表替代真实 SDK 可快速跑通逻辑,但若想验证业务层“确实请求了指定维度的特征”,还需要 Mock 来记录请求参数。

具体案例:在特征调用处注入 Mock 的 FeatureClient,断言 get_feature("user_tag", user_id=1024) 被调用且包含 recency 维度,同时用 return_value 预设特征字典,让下游排序逻辑稳定运行。最终测试总耗时从 12 秒降到 0.3 秒。

Python 演示

python53 行
# demo_mock_stub.py# 演示 Mock 与 Stub 在 Python unittest.mock 中的两种用法from unittest import mock  class OrderNotifier:    def __init__(self, email_sender):        self.email_sender = email_sender     def notify(self, order_id, user_email):        subject = "订单 %s 已发货" % order_id        self.email_sender.send(user_email, subject)        return "sent:%s" % order_id  class DiscountService:    def __init__(self, policy):        self.policy = policy     def final_price(self, order_id, base_price):        rate = self.policy.get_discount(order_id)        return base_price * rate  def test_notify_uses_mock_to_verify_behavior():    # 创建 Mock 替身:记录 send 调用过程    mock_sender = mock.Mock()    notifier = OrderNotifier(mock_sender)     result = notifier.notify(10086, "alice@example.com")     # 行为验证:断言 send 被调用且参数正确    mock_sender.send.assert_called_once_with(        "alice@example.com", "订单 10086 已发货"    )    assert result == "sent:10086"  def test_final_price_uses_stub_to_provide_value():    # 创建 Stub 替身:只提供固定折扣率,不关心是否被调用    stub_policy = mock.Mock()    stub_policy.get_discount.return_value = 0.8     service = DiscountService(stub_policy)     # 状态验证:只断言最终价格    assert service.final_price(7, 100) == 80  if __name__ == "__main__":    test_notify_uses_mock_to_verify_behavior()    test_final_price_uses_stub_to_provide_value()    print("两个测试均通过:Mock 验证了行为,Stub 提供了数据")
目的:用两个测试演示 Mock(验证调用行为)与 Stub(预设返回值)在真实工程代码中的标准写法。
  1. mock_sender = mock.Mock()创建万能替身,默认能接收任何方法调用并记录过程。
  2. mock_sender.send.assert_called_once_with(...)这是 Mock 的“翻账本”动作,断言 send 恰好被调用一次且参数精确。
  3. stub_policy = mock.Mock()同一个类也可以当 Stub 用,关键在于你后续如何配置和断言。
  4. stub_policy.get_discount.return_value = 0.8Stub 的“喂饭”动作,设定返回值为固定折扣率 0.8。
  5. assert service.final_price(7, 100) == 80状态验证:只关心计算结果是否正确,不检查 policy 是否被调用过。

常见误区

误区我在代码里都是用 `mock.Mock()` 写测试,既设返回值又做断言,它既像 Stub 又像 Mock,所以这两个词没区别。

正解工具可以被复用,但概念不能混淆。Stub 的职责是“给定输入返回预设输出”,它不关心自己是否被调用、被调用几次;Mock 的职责是“记录调用过程并允许断言”。同一个 Mock 对象,只设返回值时它扮演 Stub,加上 assert_called_* 断言时它才成为真正的 Mock。就像一把螺丝刀既能拧螺丝也能撬盖子,但撬盖子时它扮演的是撬棍。

为何常见:因为 unittest.mock 和 Mockito 都让一个对象同时具备两种能力,新手只看 API 不看语义,自然以为二者等价。

误区单元测试要隔离一切依赖,所以数据库、内存缓存、配置文件、时间函数全部用 Mock 替换,这才算“干净”的测试。

正解过度 Mock 是测试腐化的头号原因。测试的价值在于验证真实行为,而真实行为的一部分恰恰由依赖的交互定义。能用真实组件(如内存数据库 SQLite、本地测试容器)跑通的就别 Mock;Mock 应该留给“真实组件太慢、太贵、有副作用”的场景(如邮件服务、第三方支付 API)。替身越少,测试越接近生产真相。

为何常见:教材里大量强调“隔离依赖”,初学者把“隔离”理解成“全部替换”,忽视了测试的根本目标是信心而非纯度。

误区Mock 能记录所有调用,那我多写几个 `assert_called_with` 就能把测试做得更严格,防止任何一步逻辑出错。

正解行为验证应该只针对“对外部世界产生可观察影响”的调用,比如发了邮件、写入消息队列、调用了第三方 API。内部计算步骤的中间值、临时变量组合都属于实现细节,验证它们会让测试与实现强耦合——以后你优化内部代码结构时,行为没变但测试全红,这被称为“测试过于耦合实现”。

为何常见:新手刚会写 verify 时容易上瘾,觉得断言越多越安全,忘了测试应该聚焦“契约”而非“脚注”。

误区我写了个内存版数据库类,它实现了真实的增删改查逻辑,这不就是一个功能更强的 Mock 吗?

正解Fake 是有真实业务逻辑的简化实现,它的行为是“真正工作的”,只是运行在内存中;Mock 没有业务逻辑,它的行为完全由你的脚本预设。用 Fake 替换真实数据库测的是“数据访问层在真实查询语义下是否正确”;用 Mock 替换数据库测的是“上层代码是否发出了正确的调用”。二者适用层面完全不同,不能随意互换。

为何常见:Fake 和 Mock 都实现了接口、都不碰真实基础设施,表面相似性掩盖了“有无真实逻辑”的本质差异。

面试题

Q1. Stub 和 Mock 共同的上位概念是什么?

单选题 · easy

  • A.测试框架
  • B.测试替身← 正确答案
  • C.桩代码
  • D.断言工具

答案:B。Stub 和 Mock 都属于“测试替身”家族,它们替代真实依赖以隔离被测单元。测试框架和断言工具是实现手段,桩代码是 Stub 的别名表述,都不够全面。

Q2. Stub 的核心职责是什么?

单选题 · easy

  • A.记录调用次数并报告
  • B.验证方法调用顺序
  • C.返回预设的固定数据← 正确答案
  • D.替代整个测试环境

答案:C。Stub 的核心是“状态供给”,即入参进来、预设值出去。记录调用、验证顺序是 Mock 的职责,替代整体环境不是 Stub 的定位。

Q3. Mock 相比 Stub 多出来的独特能力是?

单选题 · mid

  • A.可以设置返回数值
  • B.可以抛出异常
  • C.验证行为发生的过程← 正确答案
  • D.自动生成单元测试

答案:C。设置返回值、抛异常都是预设行为的能力,Mock 和 Stub 都能做到。Mock 的独特之处在于“行为验证”——断言方法是否被调用、参数与次数是否符合预期。

Q4. 下列哪项属于典型的状态验证?

单选题 · mid

  • A.断言 send 被调用一次
  • B.断言返回订单金额等于八十← 正确答案
  • C.断言消息发送顺序正确
  • D.断言入参包含用户编号

答案:B。状态验证关注“返回结果是否等于预期值”,断言订单金额就是典型例子。其余三项都在检查方法调用过程,属于行为验证范畴。

Q5. 以下哪些场景更适合使用 Mock?

多选题 · mid

  • A.读取用户配置并拼接待发文本
  • B.验证邮件发送接口被正确调用← 正确答案
  • C.记录消息队列的推送参数← 正确答案
  • D.获取数据库单条固定记录

答案:BC。Mock 适合“需要确认交互发生”的场景。验证邮件发送、记录消息推送都属于行为验证,适合 Mock;读取配置、获取固定记录只需数据返回,用 Stub 更轻量。

Q6. 过度使用 Mock 可能带来哪些危害?

多选题 · hard

  • A.测试运行速度显著变慢← 正确答案
  • B.测试对实现细节过度耦合← 正确答案
  • C.接口重构时大批测试失败← 正确答案
  • D.测试执行顺序发生颠倒

答案:ABC。Mock 创建多、断言多,会拖慢测试速度、使测试与实现捆绑,接口一改测试就崩。但测试执行顺序由测试框架管理,不因过度 Mock 而颠倒。

Q7. Dummy 测试替身的主要作用是?

单选题 · easy

  • A.记录所有内部状态变化
  • B.验证参数传递顺序
  • C.占位以通过参数校验← 正确答案
  • D.返回预设的复杂对象

答案:C。Dummy 只做“占位”一件事,让构造函数或参数列表不报空值错误,不参与任何业务逻辑。记录状态、验证顺序、返回复杂对象分别是 Spy、Mock、Stub 的活。

Q8. Fake 与 Mock 的本质区别是什么?

单选题 · mid

  • A.Fake 也有真实业务逻辑← 正确答案
  • B.Fake 只能用于集成测试
  • C.Mock 必须来自第三方库
  • D.Mock 不能预设返回值

答案:A。Fake 是带真实简化逻辑的实现(如内存数据库),Mock 是行为完全由脚本预设的空壳。Mock 既可来自框架也可手写,预设返回值是 Mock 与 Stub 的通用能力。

Q9. 以下哪些属于 Mock 测试的最佳实践?

多选题 · hard

  • A.只替身化真实外部边界← 正确答案
  • B.最小化替身引入数量← 正确答案
  • C.验证具体参数而非模糊匹配← 正确答案
  • D.对所有依赖一律 Mock

答案:ABC。最佳实践强调替身仅用在真实边界(如 I/O 接口)、数量尽可能少、断言参数写具体值。对所有依赖一律 Mock 正是需要避免的过度 Mock 反模式。

Q10. 测试金字塔对替身使用的核心启示是什么?

单选题 · extreme

  • A.用尽量真实的组件跑通底层测试← 正确答案
  • B.所有层的测试都要 Mock 依赖
  • C.Mock 使用越多测试越可靠
  • D.集成测试必须完全真实无替身

答案:A。测试金字塔主张底层单元测试应接近真实、用替身隔离外部副作用,越底层越偏向真实组件、Mock 越少,整体测试才稳固。全部 Mock、越多越可靠都是完全跑偏的理解。