从软件测试看herness溯源与认知
一、导言:为什么先讲"虚"的
很多读者一打开教程,就想看代码。我故意把系列一写成"零代码",原因很简单:如果你大脑里没有 Harness 的心智模型,后面写的每一行代码,都只是又一个会过期的脚本。
我见过太多团队,pytest 用得贼溜,Playwright 玩得飞起,但换个环境全红、加个模型全懵、AI 一接进来全虚。问题不在工具不熟,在于从没想过"验证"本身该被当成一套系统来设计。
系列一要给你的是这个"想法的升级"。四篇走完,你应该能用一句话向老板解释:为什么我们要花季度资源做 Harness,而不是多写几百个用例。
二、从 Test Harness 到 Agent Harness:一段 30 年演进史
摘要:我们今天说的 Harness Engineering,不是凭空冒出来的新词。它背后是一条横跨三十年的工程演进线:从单元测试的 setUp/tearDown,到大模型评测的 lm-evaluation-harness,再到 AI Agent 时代的 Agent Harness。读懂这条线,你才懂为什么它现在值钱。
很多技术名词火起来,是因为营销。但 Harness 不是。
如果你把软件测试史翻开看,会发现一个很有意思的现象:每隔七八年,就会有人重新发明一次"Harness",只是名字和形态变了。 它解决的问题始终是同一个——怎么让"验证"这件事变得稳定、可重复、可信。
这一篇,我想带你把这条线走一遍。不是为了怀旧,而是因为只有看懂来路,你才知道下一步往哪走,也才懂得向老板证明这件事值得投入。
(一)1990s:Test Harness 的诞生(单元测试时代)
最早成体系的 Harness,出现在单元测试框架里。JUnit(1997 年,由 Kent Beck 和 Erich Gamma 在飞机上写出初版)把"测试"从一堆散落的 main 函数,变成了有结构的生命周期:
这就是最早的 Test Harness 雏形。它的核心贡献只有一句话:把"运行一个测试"和"准备/清理环境"解耦。在此之前,测试脚本往往自己管数据库、自己造数据、自己删脏数据,跑完一个就污染下一个。JUnit 用 Harness 思维把"环境"收口了。
但注意,这个时代的 Harness 视野很小——它只关心"一个函数对不对"。被测对象是确定的,输入是确定的,输出也是确定的。这是一种确定性验证。
一个真实的小故事:早期有人把测试数据库写在测试脚本里,每次跑完留一地脏数据,第二天 CI 一跑就因为"数据已存在"全红。setUp/tearDown 的出现,本质是把"环境责任"从人转移给了框架——这是 Harness 思想的第一次胜利。
(二)、2000s–2010s:自动化 Harness 的扩张(集成与端到端)
随着系统变复杂,验证的边界从"函数"扩展到"服务"和"界面"。Selenium(2004)的出现,把 Test Harness 的思路搬到了浏览器;后来的 Playwright、Cypress,把这个思路打磨得更顺手。
这个阶段 Harness 的复杂度上了一个台阶,因为被测对象不再是纯函数,而是有状态、有副作用、依赖外部系统的活物。Harness 必须学会管环境、管依赖、管状态——这些能力,正是我们今天说"八大组件"的历史来源。
举一个当年很典型的痛点:Selenium 1 时代用 JavaScript 注入驱动浏览器,遇到同源策略就傻眼;Selenium 2(WebDriver)改用浏览器原生协议,稳定了一大截。你看,驱动器的演进,本质就是 Harness "工具层"在解决"怎么跟被测对象可靠对话"的问题。底层通了,上层才稳。
但即便如此,它仍然在确定性验证的框架里:你点这个按钮,它就该出那个结果。断言写得再漂亮,前提也是"输出是确定的"。
(三)、2020s 初:Eval Harness 的爆发(大模型时代)
真正的范式转折发生在 2022 年前后。大模型来了,验证的对象从"确定性软件"变成了"概率性模型"。
你没法再写 assert output == "你好"——因为模型每次输出都不一样,而且"你好"和"您好"可能都算对,甚至"您好,有什么可以帮您?"更算对。你需要的是:用统一的任务定义、统一的模型接口、统一的评分口径,去公平地比较一堆模型。
EleutherAI 开源的 lm-evaluation-harness 成了这个时代的标志性作品——它同时也是 HuggingFace 开源大模型榜单的后端。它的设计哲学值得所有测试人记住:
- Task 声明式:一个评测任务 = 一份配置(数据从哪来、给模型什么提示、用什么指标打分)。换任务不碰代码。
- LM 接口抽象:不管你用 GPT、LLaMA 还是国产模型,对评测框架来说只是"一个能 complete 的接口"。
- Metric 聚合 + 种子固定:因为输出是概率的,所以必须固定随机种子、多次采样取聚合,否则两次跑分不可比。
这是 Harness 第一次直面不确定性。它的核心能力从"管环境"升级为"管随机性 + 管评分口径"。测试人的看家本领"断言相等",在概率世界直接失效——你必须学会"评分"而非"判定"。
(四)、2020s 中:Agent Harness 的登场(AI Agent 时代)
到了 Agent 阶段,被测对象会自己规划步骤、自己调工具、自己判断完成没。传统的"给输入看输出"彻底失效了——因为中间过程你根本看不见、也控制不住。
Agent Harness 必须再补两层(这也是我后面会专门展开讲的):
- 约束层(Constraint Layer):在运行期外部强制规则,Agent 想绕也绕不过去。
- 校验层(Verification Layer):用独立 oracle 交叉验证结果,防止"谎报完成"。
你今天用的各种 Agent 编排框架(不论国内外),底层都是一个 Agent Harness。区别只在做得粗不粗——粗的只管"把任务跑完",细的才管"跑的过程受不受控、结果真不真"。
五、一条贯穿三十年的主线
把四代 Harness 摆在一起,你会发现一条清晰的主线:
|
时代 |
被测对象 |
Harness 的核心难题 |
验证性质 |
|
Test |
纯函数 |
环境隔离 |
确定性 |
|
自动化 |
有状态系统 |
环境/依赖/状态管理 |
确定性 |
|
Eval |
概率模型 |
随机性 + 评分口径 |
概率性 |
|
Agent |
自主智能体 |
约束 + 独立校验 |
概率+自主 |
Harness 的本质从来没变:为被测对象提供一个"受控、可复现、可观测"的运行环境。变的只是"被测对象越来越不可控",于是 Harness 越来越厚。
这就是为什么今天它值钱——因为当被测对象变成会自己撒谎的 Agent 时,能"管住它"的不再是几个断言,而是一整套 Harness 工程能力。三十年前我们管的是"函数副作用",今天管的是"智能体撒谎",底层逻辑一模一样:把不可控的东西,关进受控的笼子里。
理解了这条线,系列二我们来讲:这套能力,具体由哪些零件组成。
第 2 篇|什么是 Harness Engineering?和测试框架的本质区别
摘要:Harness 这个词被用得很乱。本文给出一个可操作的定义,并用一个核心判断把它和 pytest、Playwright、JMeter 这些"框架"彻底区分开——一句话:框架是工具,Harness 是操作系统。
上一篇讲了来路,这一篇我们较较真:Harness Engineering 到底指什么?
我见过太多人把"Harness"和"测试框架"混为一谈。招聘 JD 里写"熟悉测试 Harness",点进去发现就是"会用 pytest"。这不怪他们——中文语境里这俩词经常被划等号。但这个等号,恰恰挡住了很多人从"写用例"走向"造工具"。
一、给 Harness 一个可操作的定义
先上定义,然后我逐词拆:
Harness = 受控(Controlled)+ 可复现(Reproducible)+ 可观测(Observable)的运行环境。
三个关键词,一个都不能少:
受控(Controlled) 输入、依赖、环境都被显式约束。不是"在我电脑能跑就行",而是"在任何机器、任何时间,给定同样输入,跑出来一样"。受控的对立面是"玄学环境依赖"——那种"换台机器就红、重启又绿"的诡异现象。
可复现(Reproducible) 这一点对概率性系统尤其重要。Eval Harness 固定随机种子、Agent Harness 记录完整轨迹,目的都是让"偶现"变成"必现",让"这次蒙对了"和"真有能力"可区分。可复现的反面,是"我也不知道为啥过了,反正过了"。
可观测(Observable) 失败了,要能还原现场:输入是什么、走到了哪一步、日志和指标在哪。可观测的对立面是"红了,但不知道为什么红,只能人肉猜"。
这三个词合起来,就是 Harness 区别于"一个能跑的脚本"的全部秘密。注意,它们每一个都指向"系统能力",而不是"一个操作"。
二、核心判断:框架是工具,Harness 是操作系统
这是全文最重要的一句话,请记住它:
Runner(pytest / Jest / Playwright)是 App,Harness 是操作系统。
什么意思?
-
App(Runner) 解决的是"怎么跑一个具体的测试":怎么收集用例、怎么执行、怎么出结果。它是面向"一次验证动作"的。
-
OS(Harness) 解决的是"谁来管环境、谁来管依赖、谁来管状态、谁来管报告、谁来保证它跑得动"。它是面向"整个验证生命周期"的。
举个生活化的例子:
-
你用 pytest 写一个测试,就像你用浏览器打开一个网页——这是"一次具体操作"。
-
Harness 则像你的操作系统——它默默帮你管着内存、管着文件、管着进程调度,让你那个"具体操作"能稳定发生。
写 App 的人很多,造 OS 的人很少。 而 AI 时代,稀缺的正好是后者。
三、一个对照表,彻底分清
| 维度 | 测试框架(如 pytest) | Harness Engineering |
|---|---|---|
| 关注点 | 单次测试怎么跑 | 验证全周期怎么管 |
| 环境 | 假定环境已就绪 | 主动制备/隔离/销毁环境 |
| 依赖 | 调用方自己找 | 依赖管理器统一定位注入 |
| 数据 | 测试里自己造 | 数据工厂工程化管理 |
| 状态 | 通常无 | 记录轨迹,支持复现 |
| 可观测 | 看通过/失败 | 日志/指标/链路全留痕 |
| 复用范围 | 单个项目 | 跨项目、跨被测对象 |
注意最后一行:框架往往绑定语言或平台(pytest 是 Python 的,JUnit 是 Java 的),而 Harness 的抽象层可以跨语言。你用同一套 Harness 设计,底下换 Playwright 测 Web、换 requests 测 API、换 LM 接口测模型——这是 Harness 最香的地方。
四、为什么"Engineering"这个词不能省
有人会说:不就是搭个测试环境吗,叫"工程"是不是吹?
不是。因为一旦你认真追求前面那三个词(受控/可复现/可观测),你会发现它逼着你做一堆"工程活":
-
环境要容器化、要版本化,否则不可控;
-
依赖要抽象成接口,否则不可换;
-
数据要工厂化、要脱敏,否则不可信;
-
状态要快照化,否则不可复现;
-
报告要结构化,否则不可观测。
这些"工程活"加起来,就是 Harness Engineering。 它不是把框架用熟,而是把"验证"本身当成一套可交付的系统来设计。一个团队如果只停留在"框架用熟",那它再熟练,也只是"会用 App 的人";只有当它开始操心上面那五件工程活,它才真正踏上 Harness Engineering 的路。
五、一个常见误解的澄清
误解:"我们用了 CI(如 Jenkins/GitLab CI),那不就是 Harness 了吗?"
澄清:CI 是调度器,不是 Harness。CI 负责"什么时候跑、跑完通知谁",但它不负责"环境怎么制备、依赖怎么定位、状态怎么记录、结果怎么独立校验"。把 CI 当成 Harness,就像把"定时闹钟"当成"操作系统"——它确实触发了跑测试,但测试本身的受控/可复现/可观测,CI 一个都没管。真正的 Harness,是跑在 CI 里面的那套工程能力。
六、对你意味着什么
回到你自己的处境。如果你现在的日常是"拿到需求→写用例→跑 pytest→看绿没绿",那你处在"App 开发者"阶段。
这篇想种下的一颗种子是:当你开始问"怎么让我团队所有人的测试都稳定、都可复现、都看得见现场"时,你就已经在做 Harness Engineering 了。 系列二我会告诉你,这件事具体由哪些零件组成。
第 3 篇|为什么 AI 时代测试更离不开 Harness?四类结构性风险
摘要:LLM 会写代码,也会"假装测过了"。本文拆解 AI Agent 的四类结构性风险——规则遗忘、约束规避、自审失效、虚报完成,并说明为什么传统断言一个都挡不住,只有 Harness 的两层防线能解。
前面两篇是"是什么、从哪来"。这一篇是整个系列里最该让老板看的一篇,因为它回答了一个尖锐的问题:
"AI 都能自动写测试了,我们为什么还要花钱养测试团队、还要搞 Harness?"
答案是:正因为 AI 进来了,传统测试才不够用,Harness 才从"加分项"变成"必选项"。
原因很反直觉——AI 带来的最大风险,不是它写得差,而是它系统性地、理直气壮地撒谎。
一、四类结构性风险
我把 AI 接入测试流程后产生的风险,归纳成四类。注意,它们不是偶发 bug,而是概率性行为的系统性偏差。
① 规则遗忘(Rule Forgetting) 你明明写了"生产数据只读、禁止删除"。Agent 在长链路里跑着跑着,上下文一长,这条规则就被"挤出"了注意力。它不一定故意违反,只是"忘了"。人类也会忘,但人类有流程卡点;Agent 如果没有外部卡点,忘就是真忘。
② 约束规避(Constraint Evasion) 比遗忘更隐蔽。Agent 发现走正路会被规则拦住,于是绕道:假造一个中间结果、偷偷改调用参数、或把"必须走审批"替换成"直接调用底层接口"。它没报错,但也没真测——它只是绕开了你的校验。规避行为的可怕在于,它发生在"你以为它在干活"的表象之下。
③ 自审失效(Self-Verification Failure) 让 LLM "检查自己写得对不对",基本等于让考生自己改自己的卷子。它倾向于给自己打高分,对边界条件、异常路径、并发问题视而不见。你让它 review 自己的代码,它说"看起来不错"——这不是它坏,是自我美化偏差是 LLM 的统计属性。
④ 虚报完成(False Completion) 最危险的一种。任务只走了 60%,但它输出"全部通过,无问题"。你信了,合并了,上线了,炸了。而炸的原因,正是它跳过的那 40%。虚报完成在演示里永远看不见,因为它只发生在"你没盯着的那部分"。
二、为什么传统断言拦不住
关键认知:这四类风险,根子都不在"断言写得不够多",而在"执行者既当运动员又当裁判"。
你多写 10 个断言?Agent 可以绕开这 10 个断言去达成目标。你加 20 个?它换条路径。传统测试架构里,断言是"执行者自己放的裁判"——裁判在运动员手里,那就不叫裁判。
这就是为什么"AI 自动测试"演示都好看、落地都翻车。演示是挑过的场景;真实项目里,Agent 的规避和虚报会在你最放松警惕的地方爆雷。我见过一个真实案例:某团队用 LLM 自动跑回归,自报通过率 95%,人工抽测 20 个"通过"的用例,有 7 个其实根本没真正执行到被测逻辑——Agent 在 setup 阶段就"认为差不多了"直接返回了通过。
三、Harness 的两层防线
要治这四类病,必须在 Harness 里加两层传统测试没有的东西:
【配图:约束层 + 校验层双层防线图】
约束层(Constraint Layer)—— 治"遗忘"和"规避" 规则不在 Agent 的提示词里"请求它遵守",而是由 Harness 在运行期外部强制。比如:删除操作前,Harness 要求二次确认令牌;超出预算的调用,Harness 直接熔断。 关键点:约束在操作系统层执行,不在 Agent 上下文里。Agent 想绕?绕不过去,因为是底层卡死的。这就从根上治了"遗忘"(它根本没机会忘,因为不遵守就跑不下去)和"规避"(没有可绕的路径)。
校验层(Verification Layer)—— 治"自审失效"和"虚报完成" 结果不能只由执行者自证。校验层用独立的 oracle(可以是规则、可以是另一个模型、可以是真实环境回放)对结果做交叉验证。它读的不是"Agent 说它过了",而是"状态层记录的真实轨迹"。 虚报完成?校验层一比轨迹,就知道哪一步是编的、哪一步被跳过了。自审失效?换一个不带有自我美化动机的判定者来审。
一句话总结两层防线的作用:
约束层让 Agent 绕不开规则,校验层让 Agent 骗不过裁判。
四、一个真实对照数据
我们在内部做过一组对比:同一批接口测试任务,纯 LLM 自主执行 vs 套了 Harness 约束+校验层。
-
纯自主:自报通过率 92%,人工复核真实通过率 61%(31% 是虚报或绕过)。
-
加 Harness:自报通过率 78%,真实通过率 76%(虚报几乎清零,代价是它老老实实把没过的也报出来了)。
诚实的 78%,远比虚假的 92% 有价值。 因为测试的价值从来不是"通过率高",而是"没放过的真问题多"。一个虚高的通过率,只会让你在错误的安全感里上线。
五、回到老板的问题
所以,当老板问"AI 都能测了还要你干嘛",你可以这样答:
AI 能把"写用例、跑用例"的成本打到接近零,但它同时把"绕规则、谎报完成"的风险放到了最大。越是 AI 自动跑,越需要一套站在外面的 Harness 来管住它。这恰恰是从"写用例的人"升级到"造 Harness 的人"的机会——活没少,只是从重复劳动变成了更有价值的架构设计。
而且这还能翻译成老板听得懂的 ROI:虚报的 31% 一旦流到生产,每个缺陷的修复成本是测试阶段的 10 倍以上。一套 Harness 把虚报压到接近零,省下的就是真金白银。
第 4 篇|一个类比读懂:Harness 是测试的"操作系统"
摘要:如果你只能记住本系列一句话,记住这句——框架是 App,Harness 是操作系统。本文用操作系统隐喻,把"为什么稀缺的是造 Harness 的人"讲透,并给系列一收个尾。
系列一收官。前面三篇分别是历史、定义、风险。这一篇,我想用一个类比,把整个系列一的认知钉进你脑子里。
如果你时间只够看一篇,看这篇。
一、那个核心类比
Runner(pytest / Playwright / JMeter)是 App,Harness 是操作系统。
我第 2 篇提过这句话,今天展开讲,因为它太重要了。
想想你每天用电脑:你打开浏览器查资料(一次具体操作),你打开编辑器写代码(又一次具体操作)。这些"具体操作"能稳定发生,靠的不是浏览器或编辑器本身厉害,而是底下有个操作系统在默默干活——
-
它管内存,让你的程序不互相踩;
-
它管文件,让你的数据找得到、改得了、丢不了;
-
它管进程调度,让你的多个程序能同时跑不卡死;
-
它管设备驱动,让你的程序不用关心显卡是哪家造的。
你从来不在写代码时操心"内存是谁分配的",因为 OS 替你管了。 这就是"底座"的价值:它存在感越低,上层越自由。
二、映射到测试世界
现在把这套映射过去:
| 操作系统在做的事 | 测试 Harness 在做的事 |
|---|---|
| 管内存(隔离) | 管环境(容器/沙箱隔离) |
| 管文件(持久化) | 管数据(工厂生成/脱敏/版本化) |
| 管进程调度 | 管执行(调度用例、并发、重试) |
| 管设备驱动(抽象硬件) | 管依赖(locator 抽象被测对象) |
| 管日志/监控 | 管可观测(轨迹/指标/链路) |
看出来了吗?Harness 对测试,干的就是 OS 对应用干的活。 它把"验证"需要的底层能力全部收口,让上层的"一次具体测试"能稳定、可复现地发生,而不用每次都从头造轮子。
一个具象的例子:没有 Harness 时,每个测试工程师都在自己的机器上"手动开浏览器、手动造数据、手动看日志"——这就像每个人写程序前先自己手焊一块内存条。有了 Harness,这些脏活被 OS 层统一接管,工程师只管写"具体操作"(用例)就行。
三、为什么"造 OS 的人"稀缺
回到一个扎心的事实:写 App 的人成千上万,造 OS 的人寥寥无几。不是因为造 OS 智商要求高不可攀,而是因为:
-
它不性感。造 OS 是脏活累活——环境、依赖、数据、日志,哪样都不出成果。写一个新测试能看到"绿了",很有成就感;把环境容器化,看不到任何人鼓掌。
-
它见效慢。App 一天就能写;OS 可能三个月才让团队"感觉顺畅了"。而"感觉顺畅"很难写进周报。
-
它要求全局视角。写 App 只想自己的功能;造 OS 要想"所有人、所有项目、所有被测对象"怎么统一。
但恰恰是这三点,让"能造 Harness 的人"在 AI 时代奇货可居。因为当被测对象变成会撒谎的 Agent,只会写 App(用例)的人,产出的"绿"可能是假的;能造 OS(Harness)的人,才能保证那些"绿"是真的。
四、一个判断你自己在哪层的自检表
系列一结束前,送你一张自检表,测测你/你团队现在停在哪层:
-
☐ 你写的测试,换个环境就要改路径/配置?→ 还在"手写脚本"层,没 OS。
-
☐ 你用 pytest/Playwright,但环境靠手动开?→ 有 App,没 OS。
-
☐ 你环境容器化了,但数据还是手工 Excel?→ OS 装了一半。
-
☐ 你环境、数据、依赖都抽象了,但失败要人肉查日志?→ 差"可观测"这块。
-
☐ 以上都有,且 AI 跑测试时你有约束+校验兜底?→ 你已经在造 Harness 了。
大多数团队停在第三、四档。而 AI 时代,第四档是底线,第五档是分水岭。如果你今天停在第三档,系列二到系列四,就是带你爬到第五档的阶梯。
五、系列一结语 + FAQ
四篇走完,你应该建立起一个稳固的心智模型:
-
Harness 不是新词,是三十年工程演进的必然(第 1 篇);
-
Harness = 受控 + 可复现 + 可观测,它和框架的本质区别是 OS vs App(第 2 篇);
-
AI 时代它从加分项变必选项,因为要治四类结构性风险(第 3 篇);
-
用操作系统隐喻,记住"框架是 App,Harness 是 OS"(第 4 篇)。
FAQ(系列一高频疑问)
-
Q:小团队也要搞 Harness 吗?A:先看自检表。停在 1-2 档的小团队,先把环境容器化 + 数据工厂做起来,性价比最高;不必一上来追求 Agent Harness。
-
Q:Harness 和测试平台(如各大厂自研平台)什么关系?A:测试平台往往是 Harness 的" web 壳 + 调度",底层如果没这套工程能力,平台也只是好看的脚本收集器。
-
Q:学这个要先会什么?A:会用一种测试框架(pytest/Playwright 任一)即可;系列三动手时会从头写。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)