首页/开发/软件测试速查/过度打桩的问题
自动化测试66 条

过度打桩的问题

过度打桩让用例与实现细节强耦合:调用顺序、内部方法名变化都导致失败,而真实回归价值有限;务实的边界是只隔离外部依赖(网络、时间、随机),业务逻辑用真实对象。

软件测试速查开发更新于 2026-10-13

过度打桩是什么

过度打桩指测试把被测对象内部的协作对象也替换成桩,用例因此与实现细节强耦合:调用顺序、内部方法名一变就失败,而对真实缺陷的检出价值有限。务实的边界是——只隔离外部依赖(网络、数据库、时间、随机、第三方 SDK),业务逻辑尽量使用真实对象。判断标准:替换的是进程外或不可控的依赖,还是被验证对象自己的一部分。

基本信息

问题本质测试与实现耦合,重构即红灯,信号噪声大
可以替换网络与数据库、时间与随机、第三方 SDK、消息队列
不要替换被测对象的内部方法、同进程的业务对象
替代方案容器化依赖、内存实现、契约测试、录制回放
预警信号重构时大面积改测试,说明打桩过度

典型使用场景

  • 外呼支付接口:用桩替代网络调用,避免不稳定与真实费用
  • 时间相关逻辑:替换时钟实现,稳定构造月末、跨年、夏令时场景
  • 随机与唯一标识:注入可控生成器,让断言可预期
  • 数据库依赖:用容器或内存实现替代,或仅在集成层用真实库
  • 第三方 SDK:通过适配层打桩,避免测试耦合 SDK 内部行为
  • 重构评审:统计用例对内部实现的依赖数量,作为改进指标

只替换外部依赖的写法

只替换外部依赖的写法
# 只替换时钟,计价逻辑走真实实现
def test_fee_at_month_end(monkeypatch):
    monkeypatch.setattr("app.clock.now", lambda: datetime(2026, 2, 28, 23, 59))
    assert calc_fee(order) == expected
# 反例:把被测对象的内部方法也替换掉,用例与实现细节绑死
# monkeypatch.setattr(Order, "calculate_tax", lambda self: 1)
# 一旦内部方法改名或拆分,这条用例就会失败,但它并不代表业务错了

替换边界对照

对象建议做法理由
网络与数据库用桩或容器、内存实现外部依赖,慢且不可控
时间与随机替换为可控实现不可控输入,用例需可复现
第三方 SDK先加适配层再打桩隔离合作方接⼝变化
被测对象内部方法避免替换,走真实实现属于被测行为本身
与实现细节绑定的用例会在重构时集中失败,团队会逐渐失去对测试信号的信任:要么放弃维护,要么全员忽略红灯,这才是过度打桩最大的代价。
边界不是「零替换」:外部依赖必须隔离,否则测试又慢又脆。判断口诀:替换的是进程外或不可控依赖;被验证对象内部的一切都走真实路径。

易错点

  • 连业务对象都打桩:测试退化成验证桩被调用过,真实计算错误无人发现
  • 断言调用次数与顺序:实现一改就失败,维护成本高而检出价值低
  • 用桩造理想数据:掩盖序列化失败、字段命名不一致等真实集成问题
  • 直接替换第三方 SDK 内部函数:SDK 升级后用例成片失败,应先封装适配层再打桩
  • 集成层也全用桩:接口契约变化无人发现,需要契约测试或少量端到端用例兜底

数据来源与参考

  • Mockito 官方文档(Mocking 边界建议)
  • Martin Fowler《Mocks Aren't Stubs》(martinfowler.com)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌