本节摘要:单元与集成不是二选一,而是一条分层防线的两段保险。本节讲清二者如何互补——单元保"快而多",集成保"真而深"——并给出一条从提交到发版的测试布线顺序,以及"避免重复测试"的正确含义。
把单元和集成对立起来,等于让一层防波堤的二段互相抢工。真实的防线是一条纵向链路:单元层像内圈护栏,逐件拦住逻辑错;集成层像外圈城墙,堵住接缝漏进来的。只有两层一起上,才会让"单件内部"和"件件之间"都有人负责,这是互补性的第一层意思。
先想清楚两层各自的盲区,互补性就自然浮现。单元层因为隔离,看不见接缝、数据口径、进程协议;集成层因为高保真,看不到单件内部的每条分支与极端场景。前者的盲区是后者的主场,后者的盲区是前者的主场——一个抓逻辑密度,一个抓交互真相。所以两者加在一起,才覆盖了"内部逻辑 + 相互约定"两个维度的风险空间。
单元层快、多、廉,负责高频回归;集成层慢、少、贵,负责关键路径深检。二者天然适合不同的节奏——日常把廉的单元灯多开几轮,关键时刻再把集成灯点一遍做终检。这不是重复消费,而是把正确的灯用在对的频率上:既不让廉的灯缺席日常,也不让贵的灯挡在每次提交前。
下面这张图画出从提交到发版,两层灯轮流值守的布线顺序。

常听的一句话是"单元测过了,集成就没必要再测"。这句要打折扣。它的本意不是"集成就别再碰这段逻辑",而是"别把单元已验证过的具体分支在集成层原样抄一遍"。集成层该做的,是验证"这段逻辑的输出能不能被对面正确消费"——那是单元层想查也查不着的。所以正确的"避免重复"是:每层只验自己能验的那一半真,不越界也不漏界。
第一,把单元与集成测试分目录、分 CI 关卡管理,谁红谁的信号才清晰。第二,让公共 fixture(初始化、数据种子)尽量复用,别在两套里各写一份维护两遍。第三,用报告把两层结果并成一页看板——单元绿了几条、集成拦了几类——让"整条链路都亮"成为可跟踪的状态,而不是玄学。
把互补落到时间轴上最有说服力。假设一个改动横跨纯逻辑与真库接缝,一次正常的布线大致是这样:提交前,只跑单元层,争取在几十毫秒内拿到最要快的反馈,这段只靠廉的护栏,绝不拉满整套;合并到主分支后,交给 CI 的"快集成"档——用容器起的真库把接缝走一遍,十几分钟内出结论;发布前那一晚,才把"慢集成 + 端到端冒烟"整体点燃,作为最后一层防线。
留意这套布线里单元层和集成层各登场了一次但职责绝不重复:单元在最早、最廉的窗口拦逻辑错;集成在合并后和发布前,用真依赖拦接缝错。它们像接力跑:同一个 bug,先被单元反手扇一巴掌,漏了再由集成兜住,落到用户头上时已经没有手可以漏了。
互补设计得再漂亮,也会在长期运行里悄悄失效,给两个可观察的信号提前预警。其一,"集成红灯开始普遍早于单元红灯"——这往往意味着单元层因为接口变动频繁、已经写不出贴合的用例而逐渐空心化,大家开始默认把验证都推到集成层借力。此时不是集成太好,而是单元屋漏。其二,"单元层绿灯 + 集成层绿灯都在,上线后仍爆接缝事故"——这多半是"两层虽绿但测的是同一件事",即违反了前面"避免重复"的边界,两层其实都在抄单元分支,谁都没真测接缝。两个信号出现任何一个,都该回头重查分工,而不是继续往倒霉的层猛堆用例。
前面讲了两个预警信号,这里再给四个容易踩的"协同坑",方便对号入座。一是**"单元俱备、只欠集成":单测堆得很满,可一门心思想着"单测过了不用集成",接缝长期裸奔,上线才出事——这是最常见的自我安慰。二是"集成当主编,单元跑龙套":什么都甩给集成用例,单元层空心化,日常反馈被拖进分钟的节奏,改动一多就堵。三是"两层各写各的 fixture":同一份种子数据在单元和集成两套里各维护一遍,改一处忘另一处,两条灯一时红一时绿,谁也说不清该信哪盏。四是"看板只显示绿/红,不显示覆盖面"**:两层都亮绿,却没人真正核对"接缝到底有没有被任何一层覆盖到",于是出现"双绿仍漏检"。
这四个坑的本质,都是没守住"每层只验自己能验的那一半真"这条边界。预防不难——给每个高风险接缝指定"唯一归属层",复核一次"有没有任何层漏掉它",再让看板把"覆盖情况"和"红绿状态"分列,别让"亮绿"变成判断覆盖的唯一依据。防线要真正协同,靠的就是这类一次次落到细节的核对,而不是一句"我们两个都测"的漂亮话。
互补关系中还藏着一层容易漏掉的默契:两层不仅在时间上接力,还应该互相"挑刺"。单元层绿了不必然等于接缝一定太平,要记得让集成灯问一句"你单元验过的那段,真的被对面正确消费了吗";反过来,集成层绿了也不该让单元层松懈,单元灯要反问"核心分支和极端值,真依赖过一遍就都覆盖到了吗"。这种互相追问看似唐僧念经,实则是把"每层只验自己那一半真"落到推翻:任何一层都不把"另一层绿过"当作自己可以偷懒的理由。真正健康的防线,是两层各守边界,同时都保留一句"我信你,但接缝风险我要再核一次"的余量。
把这一节收成一句话:单元与集成的协同,终点不是"两个都测、谁都不管重叠",而是**"每层分工清楚、又靠共用接口互通有无"**。分工清楚,保证无死角也无重复;互通有无,保证公共契约、测试数据、看板状态在两层间流动,而不是各自闭门造灯。做到这两点,整条链路才会像前面布线图那样,每一段都被恰到好处的一盏灯照到,且每一盏灯都清楚自己照的是哪一段。
第 4 章把分工与互补讲清了,第 5 章我们走进手术室——不再手动开灯,而是让 TDD 把红灯先行变成日常、让代码天生好测、把所有灯接进 CI 自动亮。