想象你去体检中心做年度检查。你手里拿着一张检查单:血常规、心电图、腹部B超、胸片……每个项目都有严格的步骤和判断标准,一项项查过去,最终汇总成一份健康报告。软件开发中的“测试用例生成”,本质上就是训练“机器医生”——让工具自动为代码安排体检项目,自动判定哪里出了毛病。 但这台“机器医生”也有不同流派。有的像“随机抽查派”:拿针管四处乱扎,扎到哪算哪(Randoop);有的像“循证优化派”:不断调整体检方案,把覆盖率越做越大(Evosuite);还有最新的“AI 专家派”:学习几千个项目的历史病例,直接预测哪里最可能出问题(AI-driven)。它们各有所长,也各有短板。这篇文章,就带你读懂这三种测试生成武器的原理、指标和实战选择。
对 AI 从业者而言,掌握自动化测试生成能力意味着能用算法替代重复劳动、用数据驱动质量保障,是通向智能软件工程的核心技能之一。
目录
可视化演示
展示Evosuite搜索式测试的覆盖率增长、随机与AI驱动测试对比及覆盖率与缺陷发现率的关系。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Evosuite搜索式测试 青色块为已覆盖代码,遗传算法逐代提升覆盖率 橙色光点为发现的缺陷位置 fitness = coverage + branch_distance 适应度函数引导测试生成方向
测试基础:测试用例与测试套件
去体检中心做全面检查,你会拿到一张检查单:血常规、心电图、腹部B超、胸片……每一项都有严格的步骤和预期标准。软件开发中的“测试用例”与此惊人相似——它由三部分构成:一组输入数据、执行的前置条件、以及期望产生的输出。比如给一个计算器函数设计测试用例,输入 (3, 5),期望输出 8。没有预期输出,测试就失去了判断依据。
把许多测试用例像体检套餐一样打包在一起,就组成了“测试套件”。套件不是简单堆砌,它讲究覆盖面:既要有查“数值计算”的用例,也要有查“异常输入”的用例。而判断每条用例是否正确,需要一个“测试预言”——好比体检报告上的正常值参考范围。在代码世界里,预言可能是一句断言、一个“不应抛出异常”的约定,或者一个对象不变量(比如排序前后集合大小不变)。
为什么软件行业如此重视自动化?在现代持续集成流水线里,代码每天被修改几十次,靠人工手工测试根本来不及。自动化测试能在几分钟内执行成百上千条用例,把工程师从重复劳动中解放出来。测试用例生成工具要解决的,正是“如何让机器自动写出这套体检单”——有了它,每次代码变更后都能立刻重新体检一遍。
测试用例就是代码世界的体检项目,套件是体检套餐,而测试预言则是判断指标是否达标的“参考值”。理解这个基础,后面所有工具都是在回答同一个问题:如何自动、高效地把这张体检单开出来。
- 测试套件集合$T$ 表示本次测试的全部用例,就像体检套餐包含的所有项目清单。
- 单条用例每一条 $t_i$ 由输入和预期输出两部分构成,是测试的最小执行单位。
- 下标索引$i$ 从 1 遍历到 $n$,标记套餐中第 $i$ 个项目。
随机测试:Randoop 的反馈式探索
有些体检机构提供“随机抽检”服务——在症状不明时,先随机抽样几个指标看看。但抽什么不是完全瞎蒙:医生会观察第一次抽检结果,如果某项异常,就顺着这个方向做更细的检查。Randoop 就是这套思路的软件版。
Randoop 是随机测试的代表工具,专门针对 Java 程序。它随机生成一串“方法调用序列”——比如先创建列表对象,再调用 add、remove、get 等方法——就像医生随机安排一连串检查项目。工具会持续盯着执行过程:一旦某个调用抛出了异常,或者违反了对象的不变量(比如“栈弹出后元素数量减一”),就记录这条序列作为一个潜在缺陷的测试用例。
Randoop 的精髓在于反馈导向。它随机调用方法后,会观察对象产生的新状态,再从新状态出发生成后续调用。这好比医生发现血糖偏高,立刻追加糖化血红蛋白检测。同时它执行契约检查:方法抛出不该抛的异常、违反对象内在约定,都视为红牌。正因如此,Randoop 实现简单、运行极快,对空指针、非法状态这类崩溃缺陷特别敏感。
随机抽检不是乱抽,Randoop 也靠反馈来“指哪儿打哪儿”。它的短板是缺乏对代码结构的理解,深层的业务分支很难覆盖——这恰好引出了下一类工具。
- 前序序列$t_{\text{prev}}$ 表示已经执行过的一段方法调用序列,相当于体检中已经做完的项目。
- 反馈评分$\text{FeedbackScore}(\cdot)$ 根据前序序列是否产生新状态、是否触发异常给出分数,是对该序列“探索价值”的量化。
- 正比生成$P(t_{\text{new}} \mid t_{\text{prev}})$ 表示在前序序列已知时生成新序列的概率,它正比于反馈评分,即反馈越好越倾向继续使用。
搜索式测试:Evosuite 的遗传算法
如果随机抽查不够全面,就需要“智能化定制体检方案”。想象一位经验丰富的营养师在优化你的体检套餐:保留效果好的项目,淘汰没用的,再重新组合出新方案,迭代几轮后套餐越来越完善。Evosuite 正是这种思路的代表,它用遗传算法为 Java 类自动生成单元测试。
在 Evosuite 的世界里,每个候选测试用例就是一条“染色体”,一组用例构成一个“种群”。每条染色体都要接受适应度函数的打分,分值越小越好。Evosuite 的分值综合了“覆盖层度”和“分支距离”:既看距离目标分支还差几层调用,又看当前分支的条件离满足还差多远。这一步很像体检方案评分——既看覆盖了多少器官,又看每个器官的检查是否深入。
随后进入“选择-交叉-变异”的循环:适应度高的用例被保留下来作为父母,通过交叉(交换两个用例的部分调用)和变异(随机改变某一步调用)产生后代。经过几十代进化,种群的覆盖率不断提高。最终,Evosuite 输出带 JUnit 框架的完整测试代码,并自动为用例生成断言,工程师只需复制进项目即可运行。
遗传算法就像营养师不断“优胜劣汰”地优化体检套餐,最终找到覆盖全面的方案。Evosuite 覆盖能力强于纯随机方法,非常适合为核心业务类做深度单元测试。
- 未覆盖层数$\text{approachLevel}(c)$ 表示从当前方法调用到目标分支还隔着几层未覆盖的控制流,相当于离目标器官还差几个检查步骤。
- 分支距离$\text{branchDistance}(c)$ 表示目标分支的条件(如
a > b)还差多少才能满足,是归一化后的数值。 - 加权求和$\nu$ 是调节两者权重的系数,$\text{fitness}$ 越小代表该用例越接近目标分支,越有资格遗传给下一代。
AI 智能驱动:让模型参与测试生成
当一位资深医师结合全球海量病例数据做出判断时,体检就不再是“看到什么查什么”,而是“预测哪里最可能出问题,就重点查哪里”。AI 驱动测试生成,正是用机器学习模型把这种预测能力注入测试流程。
先看强化学习。测试生成被建模为智能体与代码环境不断交互的过程:智能体每生成一个输入(动作),代码执行后返回覆盖率变化(奖励)。奖励函数中不仅包含新增的覆盖分支,还叠加了该代码区域的历史缺陷密度——那些曾经出过 bug 的地方,探索它们会获得额外激励。经过大量试错,智能体学会了“哪里风险高就去哪里探索”的策略。
再看神经网络预测与大模型生成。前者用历史缺陷数据训练分类器,预测代码中哪些语句更容易藏 bug,优先为目标区域分配测试预算;后者则利用大语言模型对自然语言和代码的深刻理解,直接根据函数签名、注释生成语义合理的测试代码。此外还有智能断言:从大量执行轨迹中自动挖掘对象不变量,替代人工逐条编写断言。
AI 医疗助手总是优先怀疑病史中最常出问题的部位,AI 测试生成器也学着把火力集中到缺陷高发区。不过 AI 生成结果不稳定,需要与覆盖率工具配合做“生成-验证-修正”循环。
- 新增覆盖率$\Delta \text{Coverage}(s, a)$ 是执行动作 $a$ 后比之前多覆盖的代码分支数量,衡量这次动作的探索收获。
- 缺陷密度$\text{DefectDensity}(s)$ 表示当前状态对应代码区域历史上每千行代码的缺陷数量,密度越高越值得重点测试。
- 总奖励$\alpha$ 控制缺陷密度的权重,两者相加得到本次动作的总奖励,奖励越大智能体越倾向重复类似策略。
模糊测试:变异与崩溃挖掘
有一种体检叫“极限负荷测试”——给身体的防御系统灌入各种意想不到的刺激,看它会不会当场崩溃。模糊测试对软件做的正是这件事:不断生成畸形输入,观察程序是否“扛不住”。
模糊测试(Fuzzing)通过随机或半随机的方式生成输入。早期的模糊测试就是纯粹的瞎猜字节流;现代模糊测试则大量使用变异策略:在合法样本上做位翻转、整数字段替换、插入危险字典词等操作。好比在正常食物中掺入可疑成分,看消化系统是否出问题。
现代模糊测试的杀手锏是覆盖率引导。每执行一个变异输入,工具都记录它覆盖了哪些新代码路径;凡是探索到新路径的输入会被保留进种子库,并在此基础上继续变异。这与 Randoop 的反馈有相似之处,但 Randoop 面向对象方法调用,模糊测试面向输入数据通道,尤其适合解析器、解压器、网络协议这类“吃外部输入”的模块。安全领域战绩斐然,大量 CVE 漏洞都由 AFL 等模糊工具发现。
给程序吃“杂粮”冲击防线,只要守不住就暴露隐患——这就是模糊测试的哲学。但它擅长找崩溃和内存错误,对“逻辑输出错了但程序没崩”的情况无能为力,所以常与单元测试配合使用。
- 种子输入$x$ 是当前合法的输入样本,如一个正常的 PNG 文件或一行 HTTP 请求,作为变异的起点。
- 变异操作集$\mathcal{D}$ 包含位翻转(bitflip)、插入(insert)、覆写(overwrite)等变换,每种操作模拟一种“改造”。
- 变异结果$x'$ 是经过随机选择的变异操作生成的新输入,程序会执行它并检测是否触发崩溃。
覆盖率指标:衡量测试的探照灯
体检报告上会标注“本次体检覆盖了哪些系统项目”。覆盖率,就是测试对代码的“体检范围”。它回答一个基础问题:这次测试到底把代码探索了多少?
语句覆盖(行覆盖)看“有多少行代码被执行过”;分支覆盖追究“每个 if-else 的真假分支是否都被走过”。语句覆盖最容易达到,但盲区也最大——一个语句被覆盖,不代表它内部所有分支都被测到。这就像只测了血糖,不测糖化血红蛋白,虽然都是“血糖相关检查”,深度完全不同。
如果想检验测试用例本身的“战力”,就要用变异覆盖:对源码做小改动生成一批“变异体”,比如把 a > b 改成 a < b,然后运行测试套件。凡是能被测试检测出差异的变异体,都说明测试有防御力。变异覆盖率 = 被杀死的变异体数 / 总变异体数,相当于“用测试来测试测试”——这是覆盖率体系里最严格的标尺。
但必须警惕覆盖陷阱:行覆盖做到 100% 也不等于没有缺陷,因为组合爆炸使得路径不可能被完全覆盖。覆盖率是必要不充分条件——它告诉我们“还有哪里没测”,却不能保证“测过的地方没问题”。所以覆盖率必须与缺陷发现率配合看待,否则就是只追求“体检项目多”而不看“查出多少病”。
体检单上“已检项目/全部项目”的比例让人一目了然哪里还没查,覆盖率就是这份检查进度单。但请记住:进度单不等于诊断报告,覆盖率达标不代表缺陷清零。
- 总分支数$\text{TotalBranches}$ 表示程序所有可达分支的数量,包括每个 if 的真假两侧、每个 switch 的各个 case。
- 已执行分支数$\text{ExecutedBranches}$ 是测试执行路径上真正走过的分支数量,需要插桩工具统计。
- 覆盖率比值$C_{\text{branch}}$ 取 0 到 1 之间的值,越接近 1 说明测试对代码分支的探索越充分。
缺陷发现率:谁更会抓真正的问题
体检方案好不好,不能只看项目多不多,更要看它真正查出了多少隐患——有些昂贵的检查项目,实际贡献很小。缺陷发现率就是衡量测试“真实战绩”的硬指标。
有效性概念:假设程序提前埋入 $N$ 个人工缺陷,测试用例集能够发现其中 $K$ 个,缺陷发现率就是 $K/N$。这个数字比覆盖率更接近测试的本质目标——找到 bug。覆盖率描述的是“看得多广”,发现率描述的是“抓得多准”。一个覆盖 100% 语句但只发现 30% 缺陷的测试套件,远不如覆盖 80% 但发现 70% 缺陷的套件有价值。
效率权衡:覆盖率与缺陷发现率通常相关,但存在边际递减:覆盖率达到 80% 后再堆用例,往往只带来很少的新发现。原因在于工具生成的用例高度冗余——100 条用例可能有 90 条都在测同一个分支。此时需要冗余消除(测试用例缩减):用启发式方法挑出最小子集,在保持覆盖率不变的前提下删掉重复用例,让测试套件更轻更快。
实证研究表明,三种方法在真实项目上各有千秋:Randoop 在时间预算少时能快速发现崩溃;Evosuite 在面向分支覆盖时表现均衡;AI 驱动方法在语义复杂项目上优势明显,但运行开销更大。聪明的团队会把缺陷发现率当作评估工具组合效果的第一指标。
真正优秀的体检是“钱没白花,毛病都查出来了”,缺陷发现率就是这条硬指标。现在我们已经认识了所有工具和指标,最后一步是学会如何组合使用。
- 播种缺陷集$D_{\text{seeded}}$ 是人工植入源码的缺陷集合,通常由变异算子自动生成。
- 捕获缺陷集$D_{\text{caught}}$ 是测试套件真正检测出差异的缺陷子集,是“测试战斗力”的直接体现。
- 发现率计算$DDR$ 介于 0 到 1 之间,越接近 1 代表测试越能有效识别缺陷,而非仅仅刷高覆盖率。
场景与实践:按需选择的组合策略
不同人群的体检方案各不相同:年轻人做基础套餐就好,高危人群要加专项筛查。软件测试也一样——没有万能工具,只有最合适的搭配。
Randoop 适用:快速冒烟。新代码合入 CI 的前几分钟内,跑一遍随机测试,重点发现空指针、越界、非法状态等崩溃缺陷。它不需要任何配置,几秒钟就能给出反馈,是开发者的“第一道防线”。
Evosuite 适用:深度单元测试。当需要围绕核心类生成高分支覆盖的 JUnit 测试时,Evosuite 是首选。它能自动探索方法间的复杂交互,把 if-else 的边边角角都照顾到,输出的测试代码可直接集成进 Maven 项目。
AI 适用场景:业务语义复杂、光靠分支覆盖不够的项目。比如记账系统“余额不足应拒绝交易”这类业务规则,传统工具难以理解,而 LLM 可以依据函数注释直接生成行为断言。若历史缺陷数据丰富,神经网络还能预测高风险模块,让测试预算花在刀刃上。
组合策略:工程中最有效的是分层组合——先用模糊测试扫一遍崩溃类问题,再用 Evosuite 补强分支覆盖,最后用 AI 方法针对核心业务逻辑生成语义化用例。同时定期用变异覆盖评估整体质量,用冗余消除控制执行成本。
为身体选体检套餐要看年龄、病史和预算,为项目选测试方案要看风险、场景和时限——方法论完全相通。掌握这些工具的适用边界,你就能像资深医师一样开出最合适的“测试处方”。
- 候选方法集$\mathcal{M}$ 是所有可用测试生成方法的集合,如 Randoop、Evosuite、AI 驱动和模糊测试。
- 综合收益$\lambda_1 \cdot C(M) + \lambda_2 \cdot DDR(M)$ 将覆盖率与缺陷发现率加权求和,表示该方法的综合质量收益。
- 效率与最优选择除以 $\text{Time}(M)$ 得到单位时间的收益,$\arg\max$ 表示选择得分最高的方法作为该场景下的最优解。
AI 应用场景
持续集成流水线中的自动补测
阶段:CI/CD 构建与质量门禁阶段
解决的问题:代码频繁变更时,人工补充测试跟不上节奏,新类常常裸奔上线。传统做法是开发者手动写测试,覆盖不全面且耗时。用 Evosuite 在流水线中自动为新增类生成 JUnit 测试,并将分支覆盖率作为质量门槛,未达标的代码不允许合入。
具体案例:某电商团队的订单服务每次 PR 合并前自动运行 Evosuite,核心服务分支覆盖率从 35% 提升到 78%;同时搭配 Randoop 做冒烟测试,合并后线上崩溃类故障减少了 30%。整个流程无需人工干预,构建耗时仅增加 4 分钟。
第三方库安全审计
阶段:漏洞挖掘与风险评估阶段
解决的问题:引入开源依赖前,手动构造畸形输入测试安全边界效率极低,传统测试工具又只验证业务功能。覆盖率引导的模糊测试可以自动投喂海量变异输入,顺着新路径深入解析器内部,找出隐藏的崩溃点。
具体案例:某安全团队在升级一个 YAML 解析库前,用 Jazzer 对目标库进行了 24 小时模糊测试,发现 3 个可导致拒绝服务的崩溃,并自动生成最小复现用例提交给维护者,成功避免了线上事故。
LLM 辅助单元测试生成
阶段:智能生成与语义验证阶段
解决的问题:业务规则测试需要理解意图,传统工具无法理解“余额不足应拒绝交易”这类语义。用大模型读取函数注释和调用上下文,直接生成含自然语言断言的测试代码,再由覆盖率工具验证补全。
具体案例:开发者在 IDE 中选中一个复杂记账函数,AI 助手依据函数签名和注释生成了 8 条 pytest 用例,覆盖正常扣款、透支拒绝、账户冻结三种状态,人工审查后直接合入仓库,单函数测试编写时间从 40 分钟压到 8 分钟。
Python 演示
# 遗传算法生成三角形判断函数的测试用例并评估分支覆盖import random def triangle_type(a, b, c): """待测函数: 根据三边长度判断三角形类型""" if a <= 0 or b <= 0 or c <= 0: return "invalid" if a + b <= c or a + c <= b or b + c <= a: return "invalid" if a == b == c: return "equilateral" if a == b or b == c or a == c: return "isosceles" return "scalene" BRANCHES = { "负值检查": lambda a, b, c: a <= 0 or b <= 0 or c <= 0, "三角不等式": lambda a, b, c: a > 0 and b > 0 and c > 0 and (a + b <= c or a + c <= b or b + c <= a), "等边": lambda a, b, c: a == b == c, "等腰": lambda a, b, c: (a == b or b == c or a == c) and not (a == b == c), "不等边": lambda a, b, c: a != b and b != c and a != c,} def fitness(tc): """个体适应度 = 该用例覆盖的目标分支数占总分支比例""" hit = sum(1 for fn in BRANCHES.values() if fn(*tc)) return hit / len(BRANCHES) def mutate(tc): """变异算子: 随机修改一个边长""" idx = random.randrange(3) return tc[:idx] + (random.randint(1, 100),) + tc[idx+1:] def crossover(a, b): """交叉算子: 交换第二个边长""" return (a[0], b[1], a[2]) def suite_cover(ts): """测试套件覆盖率: 合并所有用例触及的分支""" hit = {name for name, fn in BRANCHES.items() for tc in ts if fn(*tc)} return len(hit), hit def evolve(pop_size=60, generations=30): """进化主流程: 选择 + 交叉 + 变异迭代优化""" random.seed(42) pop = [tuple(random.randint(1, 100) for _ in range(3)) for _ in range(pop_size)] for _ in range(generations): pop.sort(key=fitness, reverse=True) elite = pop[:pop_size // 2] while len(elite) < pop_size: p1, p2 = random.sample(pop[:20], 2) elite.append(mutate(crossover(p1, p2))) pop = elite top5 = pop[:5] n, names = suite_cover(top5) print("最优用例示例:", top5[0]) print("覆盖分支:", names) print("Top5 套件分支覆盖率: %d/%d = %.0f%%" % (n, len(BRANCHES), n / len(BRANCHES) * 100)) for tc in top5: print(tc, "->", triangle_type(*tc)) if __name__ == "__main__": evolve()
pop.sort(key=fitness, reverse=True)按分支覆盖率从高到低排序,模拟自然选择中的“优胜劣汰”。elite = pop[:pop_size // 2]仅保留前一半优秀个体作为父母,劣质用例直接淘汰。random.sample(pop[:20], 2)从最优秀的 20 个个体中随机选两个作为父母,兼顾质量与多样性。mutate(crossover(p1, p2))先交换边长再随机改动,产生变异的后代,探索新的输入空间。suite_cover(top5)合并多条用例的覆盖结果,因为测试套件的真正贡献是整体覆盖率,而非单条用例。random.seed(42)固定随机种子,保证实验可复现,这是工程实践中的好习惯。
常见误区
误区我只要把行覆盖率做到 100%,线上就绝对安全了,可以把测试给结了。
正解行覆盖只说明每行代码被执行过,组合爆炸使得分支组合无法穷尽。覆盖率 100% 可能只是“每条路都快速走了一遍”,不等于“每条路走到黑都验证了”。变异覆盖实验显示,即使行覆盖 100%,依然可能漏掉 20% 以上的变异体缺陷。覆盖率更像体检单上“查了几项”,而不是“是否查出了问题”。
为何常见:覆盖率是唯一能量化展示的测试指标,管理者天然喜欢拿数字当目标,于是“手段”被偷换成了“目的”。
误区Randoop 只是随机生成几个方法调用,还不如我自己手写测试用例。
正解Randoop 的核心在“反馈导向”和“契约检查”——它根据前序执行结果引导后续生成,并能自动发现异常和不变量违反。大量实践表明它能在几分钟内抓到空指针、非法状态等崩溃类缺陷,这些往往是人工容易忽略的边界。随机策略是外壳,动态反馈才是灵魂。
为何常见:只看工具名字里的“随机”,忽略了其内部的反馈机制,低估了简单策略配合深度检查的威力。
误区大模型都能写代码了,上了 AI 测试生成工具,测试团队可以大幅裁员了。
正解AI 生成的是候选用例,正确性和完备性都无法保证,需要人工审查与修正。它解决的是“从无到有”的效率问题,而测试设计、缺陷分析、风险判断仍依赖人的经验。AI 是测试工程师的“军火库”,不是替代者——工具越强,使用者的判断力越值钱。
为何常见:LLM 在示例场景下的表现过于惊艳,让人高估了它的通用能力,低估了真实软件逻辑的耦合与复杂性。
误区Evosuite 生成的测试都带断言而且能通过,说明质量很高,直接当正式测试用。
正解生成器优化的是覆盖率,可能产出低价值用例,甚至产生“自我实现预言”——把执行结果直接复制为断言,程序逻辑有误时测试照样通过。这些用例需要人工删冗余、修正断言、结合业务语义判断合理性。
为何常见:断言语法的正确性容易让人误以为语义也正确,忽视了“测试通过”可能是工具在自我印证。
误区我写的是 Java/Python,没有内存错误,模糊测试跟我没关系。
正解现代模糊测试早已覆盖 JVM 生态(Jazzer)、Python(Atheris)、Go(go-fuzz)等场景。它不仅能找内存错误,还能发现逻辑抛异常、死循环、资源耗尽等问题。对一切接受外部输入的程序——解析器、爬虫、API 网关——模糊测试都有用武之地。
为何常见:模糊测试因 AFL 在 C 语言漏洞挖掘中的名声太响,让大众形成了“只适合底层”的刻板印象。
面试题
Q1. Randoop 的核心思想是什么?
- A.用遗传算法搜索测试用例
- B.随机生成方法调用序列并用反馈指导← 正确答案
- C.用大模型直接生成测试代码
- D.穷举所有可能的输入组合
答案:B。Randoop 是随机测试的代表,核心是“随机 + 反馈”:随机生成方法调用序列,并利用前序执行的反馈(新状态、异常)指导后续生成。A 是 Evosuite 的思路,C 是 AI 驱动方法,D 是穷举测试,均不是 Randoop 的本质。
Q2. Evosuite 主要基于哪种算法进行测试生成?
- A.遗传算法贪婪生成 JUnit 测试← 正确答案
- B.插桩模拟逐行追踪执行路径
- C.随机采样海量输入碰运气
- D.静态分析不执行直接扫描
答案:A。Evosuite 是搜索式测试的代表,使用遗传算法(选择、交叉、变异)迭代优化测试用例,最终生成带断言的 JUnit 测试。B 是覆盖率统计工具的原理,C 是 Randoop 的表层特征,D 是静态代码扫描。
Q3. Randoop 通过什么机制判断测试是否失败?
- A.对照文档中手写断言
- B.检查异常抛出与契约违反← 正确答案
- C.用 AI 预测输出合理性
- D.对比 Git 历史中的行为
答案:B。Randoop 执行“契约检查”:方法抛出异常、对象不变量被违反时即判定失败,无需人工编写断言。A 是传统单元测试方式,C 是 AI 方法,D 与测试判定无关。
Q4. 覆盖率与缺陷发现率的关系通常如何描述?
- A.严格正相关覆盖越高越好
- B.毫无关联完全独立指标
- C.相关但存在边际递减效应← 正确答案
- D.覆盖率越高缺陷数量越多
答案:C。两者通常正相关,但覆盖率达到一定水平后,继续增加用例的缺陷发现增量显著变小,呈现边际递减。A 忽略了递减,B 否定了相关性,D 混淆了“发现缺陷”与“制造缺陷”。
Q5. 变异测试中“杀死变异体”具体指什么?
- A.测试用例能检测出程序行为差异← 正确答案
- B.变异后的程序无法编译运行
- C.编译器优化自动移除了变异
- D.调试器删除变异代码行
答案:A。将源码做小改动生成变异体,若运行测试时该变异体的输出与原程序不同,则称“杀死变异体”,说明测试具备检测该类缺陷的能力。B 是变异体本身的问题,C、D 与测试无关。
Q6. 模糊测试与随机测试的核心区别是什么?
- A.模糊测试带变异与覆盖率反馈← 正确答案
- B.模糊测试有覆盖率反馈引导
- C.随机测试只能用于冒烟
- D.二者在实现上毫无区别
答案:A。模糊测试的核心是“种子变异”与“覆盖率反馈”双引擎:保留探索到新路径的种子继续变异,形成有方向的搜索。而 Randoop 等随机测试虽也有反馈,但不依赖输入变异策略。D 完全错误,C 过于绝对。
Q7. 遗传算法中适应度函数的核心作用是什么?
- A.随机初始化整个种群
- B.衡量个体优劣并引导搜索方向← 正确答案
- C.计算代码编译时间
- D.控制变异算子的频率
答案:B。适应度函数是遗传算法的指南针,它量化每个测试用例的覆盖贡献(如分支距离加未覆盖层数),让优秀的个体获得更高被选择概率。A 是初始化阶段的事,C、D 与适应度无关。
Q8. 下列哪些属于 AI 驱动测试生成的方法?
- A.强化学习引导输入生成← 正确答案
- B.神经网络预测缺陷倾向区域← 正确答案
- C.遗传算法生成 JUnit 测试
- D.大模型根据函数意图写测试← 正确答案
答案:ABD。强化学习、缺陷预测、大模型生成都属于 AI 驱动方法;遗传算法属于搜索式测试(Evosuite 的核心),不算 AI 方法。做题时注意区分“进化计算”与“机器学习”的边界。
Q9. Evosuite 生成测试用例时包含哪些核心机制?
- A.分支覆盖指导适应度计算← 正确答案
- B.交叉变异等遗传操作← 正确答案
- C.固定随机数种子重复运行
- D.自动在用例中插入断言← 正确答案
答案:ABD。Evosuite 用分支覆盖信息构建适应度函数(A),通过交叉、变异进化新用例(B),最终输出带自动断言(D)的 JUnit 测试。固定随机种子是可复现手段,并非核心机制,C 不选。
Q10. 关于测试生成的下列说法,哪些是正确的?
- A.高覆盖率是质量的充分必要条件
- B.Randoop 适合快速探测崩溃类缺陷← 正确答案
- C.组合多种方法通常优于单一方法← 正确答案
- D.变异覆盖反映用例的缺陷检测能力← 正确答案
答案:BCD。Randoop 对异常和崩溃敏感,B 正确;工程实践显示不同方法优势互补,组合策略更优,C 正确;变异覆盖用“能否杀死变异体”直接度量测试的缺陷检测力,D 正确。A 错误:覆盖率只是必要条件,覆盖率 100% 不代表没有缺陷。