本节摘要:集成测试瞄的是两个以上零件之间的接缝——接口、数据、协议、进程——而不在乎单个零件内部对不对。本节给出"三个集成点"的模板,帮你在代码里一眼认出该挂集成灯的位置。
单元测试把每个零件测得很亮,可真实线上事故里,至少有一半发生在"各自都亮"的零件肩并肩的刹那。最常见的三种:接口讨论对不上——A 模块把订单号当整数返回,B 模块按字符串截断,只差三个字符就悄悄错;数据定义不一致——两个模块对同一张表的"状态"列,一个写"1",一个读"on";时序问题——A 先写再读,B 先读再写,顺序一乱就脏。这些缺陷的共同点:任何单个零件都没错,错了的是它们之间的"约定"。集成测试就是把约定拉出来当被测对象。
一句话:测"接缝",不测"零件内脏"。零件内脏正确与否是单元层的事,集成层回答的问题是"它们连起来对不对"。为了落地,我们把接缝拆成三个可检查的集成点:
识别集成点的口诀是:看见 new 一个外部客户端,看见直连一张表,看见发起一次网络调用,这三处都是接缝所在。下面这张图把三个集成点对应的"接缝波形"画出来,方便你对照代码判断该往哪层挂灯。

有人担心集成测试会重复单元测试的劳动——单元测过 discount 了,集成还测它干嘛。答案是不重复:集成测试不重新验证单件内部的每条分支,它只验证"这个单件进来给出的结果,能不能被对面那个单件正确消费"。所以集成用例的数量天然少得多,但每一个都更贵。掂量成本的时候别嫌慢、嫌贵就砍掉集成层,那是把等着上线的炸弹留在最后手工验收时才拆。
还是折扣函数,这次它真的连了数据库去查会员等级。单元测法(换替身)与集成测法(真库)对着看:
# 单元面向:注入替身库 class MembershipRepo: def level_of(self, uid): ... def level_of(self, uid): return "platinum" # 替身,只回固定档 def test_unit_discount_with_stub(repo=MembershipRepo()): assert apply_discount_with_repo(100.0, repo) == 80.0 # 集成面向:真库进去走一遍 def test_integration_repo_real_db(): seed_client_level(uid=41, level="gold") # 准备最小真数据 got = apply_discount_with_repo(100.0, client_lookup(41)) assert got == 90.0 # 金卡折扣 assert row_count("client_level") == 1 # 唯一性也被验证
单元那半边只管规则,集成这半边连初始化、读库、口径、清理一起见证。两条腿各干各的,橙黄色那条踩在本该踩的地方。
集成测试既然要真依赖,就得在组织上跟单元层划清楚。实践里建议三点:集成测试放独立目录,别和单测混在一个夹层里,否则 CI 分档调度无从谈起;公共夹具按层拆分,单元层的替身夹具和集成层的真依赖夹具不互相拉扯、也不共享临时状态;集成用例按"被测接缝"分组命名,让红灯亮起时能一眼看出是接口、数据还是协议出了问题。这三点看起来是组织洁癖,实际是把第 3.4 节"分档调度"变成可行的前提。
不是所有集成用例都要永久留在集成层。如果一条集成用例连续数月从不亮红、又明显拖慢整体轮次、且其接缝已被契约测试或单元层覆盖,就值得评估是否降级到更轻的层。反过来,如果一条单测迟迟不绿、替你造了一堆假世界,它才更该被挪到集成层用真依赖验证。这种"上下流动"不是随意,而是以"我到底要不要这一层的真相"为准绳的持续校准。
集成层里,外部服务的契约验证常落到专门的契约测试(如同类 Pact)上。契约测试和普通集成测试的分工是:普通集成测试验证"我方逻辑 + 真依赖"能不能走通;契约测试验证"我方与外部之间的接口约定"是否长期一致,即使对方没在本地起服务也能提前把变更红灯亮起来。把契约放在集成测试的前面设一道关卡,能让"外部改口径"这类最脆的闪红,在进入大轮次集成前就被截住。
| 集成测试负责 | 不负责 |
|---|---|
| 两个模块的真实对接与口径 | 单件内部每条分支的复查 |
| 数据库读写与约束的实际行为 | 用替身代替真实要比对的接缝 |
| 接口/数据/协议三点的咬合 | 重复单元层已验证的逻辑细节 |
| 提供比手工联调更省的自动验证 | 代替人工的业务判断边界 |
把这两列放进团队评审里,就能在"要不要这条集成用例"的会议上快速达成一致,不靠嗓门说话。
既然集成用例贵,就得保证每一条都物有所值,别写个"样子货"去浪费资源。最低能打的集成用例至少同时具备三样:被设立的最小真实数据(不是造一整库,而是只种被测那条路径需要的状态)、一次真实对接(真库或真网络的一次调用,而不是再包一层替身)、以及一个能指向接缝的断言(验的是"这头结果那头能不能消费",不是重复单件内的分支)。缺了这三样里任何一样,这条用例多半是在替单元层或替手工验证做重复功。
顺带一个容易踩的坑:很多人会把集成用例写成"真库 + 一串万能断言"的大杂烩,跑一次要拉起全套基础设施。更省的做法是给每条集成用例配独立的、最小化的真依赖——启动一个临时库而非整个集群,种一行种子而非灌整表。集成测试同样讲究快,只是它的"快"是相对手工联调说的,并没给你挥霍的理由。