期末的编程实操考试,张老师想挑出真正会写代码的学生。他出了一张 164 道题的试卷,每道题要求补全一个小函数。可两百份代码挨行看过去,别说一周,一个月都看不完。于是他想了个聪明的笨办法:每道题背后藏一份“评分细则”——只有几条测试用例。代码只要全部跑通,就判对;有一条失败,就判错。这就是 2021 年 OpenAI 发布 HumanEval 的底层思路,也是今天很多 AI 代码能力分数的来源。问题在于:评分细则太薄,判卷就容易被聪明的学生钻空子;覆盖太少,分数里就掺了水分。本文用一场“张老师出题、学生小陈应考”的故事,讲清楚测试用例覆盖为什么决定了大模型编程评测的可信度。

岗位关联

学懂测试用例覆盖,你就能拆解 pass@1 数字里的水分,在模型选型与评测报告中做出更可信的判断。

目录
  1. 可视化演示
  2. 评测原理基础
  3. 测试用例角色
  4. 覆盖缺口风险
  5. 用例设计策略
  6. 评分指标解读
  7. 评测质量维护
  8. 未来演进方向
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示HumanEval测试用例覆盖:功能/边界/异常用例覆盖度、覆盖度提升对评测的影响、高覆盖提升评测有效性,通过交互理解评测可信度的关键因素。

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

覆盖范围:功能-边界-异常 功能用例覆盖主路径 边界用例验证边界 Cov=Fun+Bnd 覆盖影响评测有效性

评测原理基础

教了二十年编程的张老师,这学期想从两百名学生里挑出真正会写代码的人。他连夜出了一张试卷:164 道编程题,每道题给出函数签名和一段中文描述,让学生补全实现。可当他真的开始判卷,才发现问题——程序题不像选择题,没有标准答案可以比对,两百份代码逐行读下来,他一个月都不用睡觉了。

2021 年,OpenAI 在 Codex 论文里也遇到了同样的难题。他们要评测大模型写代码的能力,于是发布了 HumanEval:164 道由人类专家手写的编程题。每道题由三部分组成:函数签名(比如 def sum_list(xs):)、任务描述(docstring)、以及几组隐藏在幕后的测试用例。和文本类评测不同,代码有一个天然优势——程序行为可以被机器客观检查,不必依赖人的主观判断。

张老师的做法,就是把“水平高低”这种模糊概念压缩成一道清晰的是非题:程序必须通过测试集里每一条用例才算正确。这个判定规则用数学写出来就是:

就这样,张老师的考试变成了一本会自己批改的试卷册。每一道题的命运,完全系在那些藏在幕后的测试用例身上——这是整个 Humaneval 评测体系的地基。

J(P,T)={1,若 ∀t∈T, 通过(P,t)0,否则J(P, T) = \begin{cases} 1, & \text{若 } \forall t \in T,\ \text{通过}(P, t) \\ 0, & \text{否则} \end{cases}
通俗说明:判定函数就是一个“一键评分器”——程序 P 必须通过测试集 T 里每一条用例 t 才算正确,否则就是零分。
  1. 程序 P 与测试集 TP 是模型生成的代码,T 是这道题背后藏着的一组隐藏用例。判定 J 的角色就是张老师的那支自动判卷笔。
    J:(P,T)↦{0,1}J: (P, T) \mapsto \{0, 1\}
  2. 全称量词 \forall要求测试集里每一条用例都必须通过,一条失败整体判零分,这正是“全部通过才算对”的严谨写法。
    ∀t∈T, 通过(P,t)\forall t \in T,\ \text{通过}(P, t)
  3. 分段取值满足条件取 1(正确),否则取 0(错误)。把模糊的能力评价压成 0/1 的明确信号,机器才能批量评分。
    J(P,T)=1  ⟺  ∀t∈T: 通过(P,t)J(P,T) = 1 \iff \forall t \in T:\ \text{通过}(P,t)

测试用例角色

出完试卷,张老师开始写评分细则。以“列表求和”这道题为例,他提笔写下了三条用例:输入 [2, 4, 6] 应该输出 12;输入 [3] 应该输出 3;输入 [-2, 3, 7] 应该输出 8。这三条用例就像是考场上的三个检查点,学生的代码必须逐点通过,才能拿到这道题的分数。

在 HumanEval 里,这些检查点是 Python 的 assert 风格单元测试。它们承担着三个角色。第一个角色是功能验证:确认代码能完成题目描述的基本任务,相当于检查学生“有没有听懂题目”。第二个角色是边界检测:试探空输入、极大极小值、特殊情况,看看代码在悬崖边上会不会失足。第三个角色是评分依据:模型从头到尾只能看到函数签名和描述,看不到任何一条测试,因此无法针对“已知答案”作弊。

一条测试用例的本质,就是一次输入与输出的严格比对:

张老师靠着这几条检查点判卷,就像厨师靠尝一口菜判断整锅汤的味道——每尝一口就多一分把握。可问题是,他只尝了三口。

通过(P,t)  ⟺  fP(tin)=texp\text{通过}(P, t) \iff f_P(t_{\text{in}}) = t_{\text{exp}}
通俗说明:一条用例就像“喂一个输入,看输出对不对”的检查点,实际输出与期望完全一致才算通过。
  1. 输入与期望输出每条用例 t 由两部分组成:输入 t_in 和期望输出 t_exp。
    t=(tin, texp)t = (t_{\text{in}},\ t_{\text{exp}})
  2. 程序实际行为f_P 表示程序 P 在给定输入下真实返回的结果,冒号左边是“程序做了什么”。
    fP(tin)=实际输出f_P(t_{\text{in}}) = \text{实际输出}
  3. 严格相等用等号要求实际输出与期望完全一致,差一个字符都算失败。assert 语句天然适合这种隐藏判卷,因为它要么静默通过,要么立刻报错。

覆盖缺口风险

考试结果出来了,学生小陈的“列表求和”得了满分,张老师却隐隐觉得不对劲。小陈的代码是把第一个元素直接当初始值,然后遍历剩余元素累加。用张老师的三条用例一测,全绿通过。可如果传入空列表 [],这段代码会直接崩溃——因为空列表根本没有“第一个元素”。

这就是典型的覆盖缺口:HumanEval 每个问题平均只有约 7.7 条用例,远远覆盖不了所有输入条件。空列表、极大整数、重复元素、特殊字符……这些边界条件就像没有设防的关卡,放走了大量“表面正确、暗藏 bug”的代码。后来研究者构建了 HumanEval+ 扩展套件,把用例扩充到原来的约 80 倍,发现不少模型的 pass@1 明显下滑——说明原来的高分里,掺着相当一部分“侥幸通过”。

为什么几条用例的差别会这么大?用数学看一眼就明白了。假设一段含 bug 的代码,面对第 i 条用例时有 p_i 的概率侥幸通过,那它通过全部 N 条用例的概率是:

小陈的成功不是实力,是运气。他那道“全绿”的答案之所以能骗过判卷,不是因为写对了,而是因为张老师的评分细则太薄——每一道没设防的边界,都是给侥幸留下的后门。

P漏判=∏i=1Npi=p1×p2×⋯×pNP_{\text{漏判}} = \prod_{i=1}^{N} p_i = p_1 \times p_2 \times \cdots \times p_N
通俗说明:漏判概率是每条用例“放走 bug 的概率”的连乘积。用例越多,乘积被乘得越小,侥幸过关的机会就越低。
  1. 单条放走率 p_ip_i 表示第 i 条用例没能拦住 bug 的概率。比如一条只用正数测 sum_list 的用例,很容易放走“空列表崩溃”的 bug。
    pi=P(含 bug 的 P 通过第 i 条用例)p_i = P(\text{含 bug 的 } P \text{ 通过第 } i \text{ 条用例})
  2. 连乘符号把 N 条用例各自放走 bug 的概率乘在一起,得到“所有用例都被骗过”的联合概率。
    ∏i=1Npi=p1×p2×⋯×pN\prod_{i=1}^{N} p_i = p_1 \times p_2 \times \cdots \times p_N
  3. 数值直觉若每条用例都有 70% 概率放走 bug,3 条用例时漏判率是 0.343,10 条时只剩 0.028。这就是扩展测试套件能挤出分数水分的原因。
    0.73≈0.343≫0.710≈0.0280.7^3 \approx 0.343 \gg 0.7^{10} \approx 0.028

用例设计策略

考试风波之后,年轻的助教主动请缨,帮张老师扩充评分细则。助教没有简单地把正数列表多加几组,而是先问了一个问题:这道题的输入空间到底长什么样?这就像一位认真的美食品鉴师,不会只吃刚出锅的那一口——还要尝尝放凉的、回锅的、加过量的盐的,才能判断这道菜真实的水准。

这种思考方式在软件测试里叫“等价类划分”:把行为相似的输入归成一类,从每一类里挑一个代表来写用例。对“列表求和”来说,正数列表、负数列表、零元素列表、空列表、超长列表,各自就是一类。每类挑一条,用最少的用例覆盖最广的输入空间。

在此基础上,还要做边界值分析。空输入、单元素列表、极大极小数值,这些悬崖边上的输入最容易暴露 bug,必须单独设卡。再往深处,还要考虑组合条件和异常输入:多参数函数要测“全真、全假、一真一假”的组合;遇到类型不匹配、超大整数、特殊字符时,代码能不能体面地失败而不是崩溃。这些策略可以浓缩成一个数学表达:

助教的新评分细则一上考场,不仅拦住了小陈的空列表崩溃,还揪出了另外三个学生的隐藏 bug。这件事让张老师明白了一个道理:好用例来自对问题空间的拆分,而不是用例数量的堆叠。

U=E1∪E2∪⋯∪Em,Ei∩Ej=∅ (i≠j)U = E_1 \cup E_2 \cup \cdots \cup E_m, \qquad E_i \cap E_j = \varnothing\ (i \neq j)
通俗说明:等价类划分就像把一整筐苹果按大小分篮——同一篮里挑一个尝,就能代表一篮的味道。
  1. 输入空间 U把程序所有可能的输入看成一个全集,边界值和异常值也都住在里面。
    U={所有可能的输入}U = \{\text{所有可能的输入}\}
  2. 互不重叠的等价类每个 E_i 是一类行为相似的输入,类与类之间互不重叠,同一类里只需挑一个代表写用例。
    Ei∩Ej=∅ (i≠j)E_i \cap E_j = \varnothing \ (i \neq j)
  3. 并成全集所有等价类拼起来恰好覆盖整个输入空间 U,不漏掉任何一类输入。
    U=⋃i=1mEiU = \bigcup_{i=1}^{m} E_i

评分指标解读

考试进入第二轮,张老师做了一个大胆的决定:每个学生可以交多份答案,只要其中任何一份通过了检查点,这道题就算答对。他给了学生 k 次机会,让学生们高兴坏了——小陈的同桌交了三次答案,第三次终于蒙对了一个边界情况,拿到了一分。

在 HumanEval 里,这个“多次机会”的设定就是 pass@k。模型为每个问题独立采样 n 个候选程序,其中有 c 个通过了全部测试用例。pass@k 问的是:如果从中随机挑 k 个,至少捞到一个正确解的概率是多少?如果用最简单的方式直接算,很容易得到有偏的估计;论文里用的是无偏估计公式:

但张老师很快发现,多给机会并不总是坏事,真正危险的是评分细则太薄。当测试用例覆盖不足时,pass@k 的每一个 k 值都被注了水——“正确”变成了“碰巧骗过了少数检查点”。机会越多,运气就越容易伪装成实力。

pass@k=E问题[1−(n−ck)(nk)]\text{pass@k} = \mathbb{E}_{\text{问题}}\left[1 - \frac{\binom{n-c}{k}}{\binom{n}{k}}\right]
通俗说明:每个问题允许模型生成 n 个候选答案,其中 c 个通过了全部用例。pass@k 计算的是“随机挑 k 个,至少捞到一个正确解”的概率,再对所有问题取平均。
  1. n 与 c 的含义n 是模型为每个问题采样的候选程序数量,c 是其中通过全部测试的个数,显然 0 到 n 之间。
    0≤c≤n0 \le c \le n
  2. 组合数分母\(\binom{n}{k}\) 表示从 n 个候选里任选 k 个的总选法数。
    (nk)=n!k!(n−k)!\binom{n}{k} = \frac{n!}{k!(n-k)!}
  3. 分子含义\(\binom{n-c}{k}\) 表示“选出的 k 个全是错误程序”的选法,两者相除就是“全错”的概率。
    (n−ck)(nk)=P(k 个全都没通过测试)\frac{\binom{n-c}{k}}{\binom{n}{k}} = P(k \text{ 个全都没通过测试})
  4. 期望符号1 减去“全错”概率得到“至少一个正确”,再对全部 164 个问题取平均,得到最终的 pass@k。
    E[⋅]=1164∑i=1164(⋯ )\mathbb{E}[\cdot] = \frac{1}{164}\sum_{i=1}^{164}(\cdots)

评测质量维护

考试结束后,张老师把助教叫到办公室,认真复盘了整套评分细则。他意识到,一份值得长期使用的试卷,必须像软件团队的测试套件一样持续维护:人工审查每一条用例是否真的表达了题意,发现漏洞就补,跑完一轮再迭代。这不是一次性的工作,而是一个反复循环。

在真实的评测体系里,这套维护动作对应着几件具体的事。人工审查:确认每条用例断言的是“正确行为”而非“错误行为”,防止测试本身写错。用例扩增:像 HumanEval+ 那样,用程序化生成加人工筛选的方式,把边界用例铺满输入空间。持续迭代:警惕数据污染——如果模型训练时见过这些用例,分数就会失真,需要定期更换或扩展测试集。

维护的效果,可以用覆盖率来衡量。覆盖率就是“被用例摸到的条件数”占“全部该摸的条件数”的比例:

经过整整一个月的维护,张老师的评分细则从每道题 3 条扩展到了 20 多条,覆盖了空值、极值、异常等所有他能想到的边界。他终于敢对学生们说:这份试卷,确实能测出真本事。

覆盖率=ncoveredntotal\text{覆盖率} = \frac{n_{\text{covered}}}{n_{\text{total}}}
通俗说明:覆盖率是“被用例摸到的条件数 ÷ 全部该摸的条件数”,比例越高,判卷越踏实。
  1. 已覆盖条件数n_covered 是测试套件实际触碰到的输入条件(边界、分支、异常)数量。
    ncovered=用例实际检查的条件数n_{\text{covered}} = \text{用例实际检查的条件数}
  2. 全部条件数n_total 是理论上应该检查的条件总数,由问题本身的复杂度决定。
    ntotal=应当检查的条件总数n_{\text{total}} = \text{应当检查的条件总数}
  3. 比值意义覆盖率趋近 1 时,第三节的漏判概率会被压到极低,评测分数才接近真实能力。
    覆盖率→1  ⟹  P漏判→0\text{覆盖率} \to 1 \implies P_{\text{漏判}} \to 0

未来演进方向

张老师的试卷在学校里出了名,其他老师纷纷来抄作业。可没过多久,最聪明的学生又开始满分了——他们摸清了出题套路。于是学校决定升级考试制度:不再只考单函数,而是考真实的工程任务;不再只有 Python,而是加入 JavaScript、Java、SQL;不再只看函数返回值,而是看整个仓库能不能跑通测试。

这正是 HumanEval 之后整个评测生态的演进路线。HumanEval 本身有天然的局限:只有 Python、只写短函数、只看单函数正确性,测不出真实工程能力。后续的 MBPP 增加了更多基础编程题,DS-1000 聚焦数据科学场景,SWE-bench 直接让模型修复真实代码仓库里的 issue——但无论怎么演进,最核心的逻辑没变:每个任务背后都藏着测试用例,用覆盖度决定对错。

在这个演进中,“覆盖”始终是地基。多语言扩展要求每种语言都有健壮的测试套件;复杂任务要求用例能模拟真实的调用上下文;仓库级评测要求隐藏测试覆盖到集成环节。再加上数据污染的追踪、动态执行的验证、智能体式评测的引入,未来的基准会越来越难“碰运气”。

张老师在学期总结上说了一句让学生们记了很多年的话:任何考试的信誉,都藏在考官看不见的评分细则里。对大模型评测来说,这句话翻译过来就是——测试用例覆盖,决定了你眼前那份漂亮分数,到底是真金还是掺了水的沙。

AI 应用场景

场景一:模型选型中的可信对比

阶段:模型评估与选型

解决的问题:直接用原版 HumanEval 对比候选代码模型,常见输入都能过、边界全漏检,分数差距可能只有一两个百分点,难以支撑选型决策。构建扩展测试套件后,才能暴露各模型在空值、极值、异常输入上的真实差异,让排名经得起复盘。

具体案例:某平台要在三个开源代码模型里挑一个作为代码助手基座,原版 HumanEval 分数非常接近且普遍虚高。团队给每个问题补充了 20~30 条边界与异常用例后,发现一个模型在空输入场景的崩溃率高达 15%,另一个模型则表现稳健,最终排名出现实质性变化,避免了上线后频繁报错。

场景二:微调数据的质量过滤

阶段:训练数据构建与迭代

解决的问题:大模型微调需要海量高质量“问题-代码”对。若只用基础用例筛数据,会把“表面正确、边界崩溃”的代码当成正样本,等于把 bug 教给模型。用扩展测试套件筛选,才能保证进入训练集的是真正健壮的代码。

具体案例:某团队微调代码模型前,从自动生成的 20 万条代码中先用原版功能用例初筛,再用扩展边界套件复筛,最终剔除了约 14% 通过初筛但边界失败的样本。训练出的模型在函数调用的空值处理上错误率下降近一半。

场景三:代码助手上线前的质量验收

阶段:部署前质量保障

解决的问题:AI 代码补全工具在 IDE 中给出的代码,如果只在常见路径上正确,开发者直接采用后容易在生产环境抛异常。QA 需要沿用评测覆盖方法论,为模型输出设计回归测试套件。

具体案例:某团队为自动生成的日期解析函数设计测试套件,覆盖闰年、2 月 30 日、时区偏移、非法字符串等边界,上线后时间解析类故障减少约 60%,验证了“覆盖设计”从评测集迁移到产品测试的有效性。

Python 演示

python58 行
def sum_list(xs):    """返回列表元素之和(空列表会抛异常,即边界遗漏的典型 bug)。"""    total = xs[0]    for x in xs[1:]:        total += x    return total  def run_case(func, inp, expected):    """执行单条测试用例,异常也算失败。"""    try:        return func(inp) == expected    except Exception:        return False  BASIC_CASES = [    ([2, 4, 6], 12),    ([3], 3),    ([-2, 3, 7], 8),] EDGE_CASES = [    ([], 0),    ([0, 0, 0], 0),    ([-5, -1], -6),    (list(range(100)), 4950),]  def evaluate(func, cases):    """返回 (通过条数, 总条数, 失败用例列表)。"""    failed = []    passed = 0    for inp, expected in cases:        if run_case(func, inp, expected):            passed += 1        else:            failed.append(inp)    return passed, len(cases), failed  def leak_probability(p_single, n_tests):    """若每条用例有 p_single 概率放走 bug,N 条时的漏判率。"""    return p_single ** n_tests  def main():    p0, n0, f0 = evaluate(sum_list, BASIC_CASES)    p1, n1, f1 = evaluate(sum_list, EDGE_CASES)    print(f"原版 3 条用例: {p0}/{n0} 通过, 失败输入 {f0}")    print(f"扩展 4 条用例: {p1}/{n1} 通过, 失败输入 {f1}")    print(f"单条用例放走率 0.7 时: 3 条套件漏判率 {leak_probability(0.7, 3):.3f}, "          f"10 条套件漏判率 {leak_probability(0.7, 10):.4f}")  if __name__ == "__main__":    main()
目的:演示“测试覆盖度决定漏判率”——同一个带 bug 的求和函数,在原版稀疏用例下全绿通过,在扩展边界用例下暴露崩溃;同时用幂运算复现正文的漏判率连乘公式。
  1. total = xs[0]直接拿第一个元素当初始值,空列表在这里抛 IndexError,是有意埋下的边界 bug。
  2. except Exception:评测时异常也按“失败”处理,和真实评测套件的容错逻辑一致。
  3. BASIC_CASES = [...]对应原版 HumanEval 稀疏的普通输入用例,只覆盖常见路径。
  4. EDGE_CASES = [...]对应扩展测试套件:空列表、全零、负数、超长列表。
  5. return p_single ** n_tests实现正文第三节的漏判率连乘公式:用例越多,漏判率越小。

常见误区

误区我觉得只要模型生成的代码能通过 HumanEval 的测试用例,就说明这段代码在真实场景里也能正常工作。测试都过了还能有什么问题?

正解测试通过只能说明代码在“被抽查到的输入”上表现正确。原版 HumanEval 每个问题平均只有约 7.7 条用例,大量边界(空列表、极大整数、特殊字符)根本没被检查。扩增用例后许多模型通过率明显下滑,正说明“测试通过”只是“当前证据支持结论”,不是“永远正确”。

为何常见:日常开发中“测试通过”常被视为可靠的完成信号,人们容易把采样验证当成全量验证,忽略了评测用例与真实输入的覆盖差距。

误区既然是覆盖度不够,那就拼命往测试集里加用例,加到几百条就绝对可靠了。量变总能引起质变吧?

正解用例需要设计而不是堆砌。如果新增用例都和已有用例属于同一个等价类(比如全是正数列表),覆盖度不会提升,反而拖慢评测。正确做法是等价类划分、边界值分析、组合条件、异常输入四管齐下,保证每条用例触碰不同的逻辑分支。

为何常见:看到“数量少导致漏判”,容易把根因误解为“数量”,而真正的根因是“覆盖的结构性缺口”。

误区让模型生成 10 次再算通过率,这不是给模型作弊放水吗?我觉得只有 pass@1 有意义,其他指标都不可信。

正解pass@k 刻画的是模型“在多次机会下找到正确解”的能力上限,对应真实场景中开发者让 AI 助手多生成几个候选再筛选的交互方式。它和 pass@1 描述不同维度的能力,两者都应参考。真正要警惕的,是低覆盖度测试套件造成的虚高,而不是 k 本身。

为何常见:人们习惯用“一次考试定录取”的思维看指标,忽略了代码生成产品天然允许“多生成、再挑选”的交互方式。

误区164 道题、每道题就那么多测试,这个数是死的,所以跑出来的分数应该很稳定,可以直接拿来做模型排名。

正解固定的测试集不代表稳定的结论。测试用例覆盖面窄时,不同模型答对的题可能高度重合又各带边界缺陷,分数差异可能只是“谁更幸运地躲过了未覆盖的边界”。使用扩展测试套件后,模型排名可能发生明显变化——可复现的只是同样的漏判,并不等于可信。

为何常见:确定性数字容易带来安全感,人们容易把“可复现”误解为“可信”,忽略了复现的可能是同一套不完整的检查。

面试题

Q1. HumanEval 基准主要用于评测哪类模型?

单选题 · easy

  • A.文本摘要生成模型
  • B.代码生成模型← 正确答案
  • C.图像分类识别模型
  • D.语音转写合成模型

答案:B。HumanEval 是 OpenAI 发布的代码生成评测集,要求模型根据函数签名与描述补全代码,所以主角是代码生成模型。文本、图像、语音模型各有自己的基准,不在 HumanEval 的考察范围内。

Q2. HumanEval 数据集中共有多少道手写编程题?

单选题 · easy

  • A.64 道
  • B.164 道← 正确答案
  • C.264 道
  • D.1024 道

答案:B。HumanEval 包含 164 道由人类专家编写的 Python 编程题。这个数量让它在早期 LLM 评测中成为标准参考,也正因为题量不大,测试用例覆盖度才显得格外关键。

Q3. 下列哪条用例最可能暴露“空列表求和”函数的 bug?

单选题 · easy

  • A.传入 [2, 4, 6] 求和
  • B.传入空列表 [] 求和← 正确答案
  • C.传入 [3] 求和
  • D.传入 [-1, 1] 求和

答案:B。如果一个求和函数直接取 xs[0] 作为初始值,空列表会抛异常。只有传入空列表的用例能触发这个边界 bug,这正是边界值分析的价值所在。

Q4. 原版测试用例覆盖不足,最主要的后果是什么?

单选题 · mid

  • A.模型训练速度变慢
  • B.错误代码被漏判放行← 正确答案
  • C.数据集文件体积变小
  • D.显卡显存占用升高

答案:B。用例少意味着许多输入条件没被检查,含 bug 的程序可能像小陈一样“全绿通过”,导致分数虚高。这与训练速度、文件体积、显存占用没有关系。

Q5. 等价类划分的核心思想是什么?

单选题 · mid

  • A.每个输入单独写用例
  • B.相似输入归为一类抽样← 正确答案
  • C.只测试边界附近的值
  • D.随机生成大量输入

答案:B。等价类划分把行为相似的输入归为一类,每类挑一个代表写用例,用最少用例覆盖整个输入空间。边界值分析是它的补充而非替代,随机生成则缺乏代表性。

Q6. 扩展测试套件相比原版 HumanEval 最主要改进了什么?

单选题 · mid

  • A.增加更多边界与异常用例← 正确答案
  • B.改用中文书写题目描述
  • C.减少题目数量提升速度
  • D.去掉函数签名降低难度

答案:A。像 HumanEval+ 这样的扩展套件,用程序化生成加筛选的方式补充大量空值、极值、异常输入用例,把覆盖度拉伸数十倍,从而挤出虚高的分数。

Q7. 关于 pass@k 指标,下列说法正确的有?

多选题 · hard

  • A.k 个样本至少一个通过测试← 正确答案
  • B.k 越大指标通常越高← 正确答案
  • C.指标与生成能力完全无关
  • D.只统计第一个生成结果

答案:AB。pass@k 衡量“生成 k 个候选里至少一个正确”的概率,因此 k 变大时概率通常不会下降、只会更高。它恰恰是生成能力的体现,也从不只统计第一个结果。

Q8. 下列哪些做法能提升测试用例覆盖度?

多选题 · mid

  • A.覆盖空输入这类边界← 正确答案
  • B.覆盖极大极小的数值← 正确答案
  • C.只复制已有的测试用例
  • D.加入类型不匹配异常← 正确答案

答案:ABD。空输入、极端数值、异常输入分别对应边界值分析和异常路径测试,能让用例摸到更多逻辑分支。复制已有用例不会带来任何新的覆盖。

Q9. 原版 HumanEval 平均每个问题大约包含多少条测试用例?

单选题 · hard

  • A.约 3 条左右
  • B.约 7.7 条← 正确答案
  • C.约 80 条
  • D.约 200 条

答案:B。可复现实验显示,HumanEval 每个问题平均约 7.7 条用例,远少于真实项目的测试密度。这也是后来 HumanEval+ 将用例扩展到原先约 80 倍的原因。

Q10. 某函数的代码在原版用例下全部通过,在扩展用例下被拦截,这说明什么?

多选题 · extreme

  • A.覆盖度直接影响评测有效性← 正确答案
  • B.该函数在任何场景都会出错
  • C.扩展用例覆盖了遗漏的边界← 正确答案
  • D.原版测试用例完全没有价值

答案:AC。同一段代码在不同测试套件下结果不同,恰恰证明原版覆盖有缺口、扩展用例补齐了它。函数在多数常见输入下仍然正确,不能说“任何场景都出错”;原版用例也仍能拦截大量普通错误,并非没有价值。