电话&微信

18600577194

​软件开发测试中的人工智能应用:在虚幻覆盖与真实质量之间

标签: 软件测试 2026-10-16 

人工智能可以辅助生成TypeScript函数的测试草稿,但代码执行过不代表业务行为已验证。本文保留折扣函数、Jest、属性测试与变异测试的示例,说明如何检查边界输入、断言和失效情况。以下代码是教学示例,不是企业交付或通用提效数据。

AI生成测试如何验证质量
原创示意图:AI生成测试如何验证质量。

自动化测试生成:当前格局

大语言模型可根据代码生成主路径和部分边界用例,但输出依赖模型版本、提示词、上下文和业务规格。评价效率时,应记录生成、审阅、调试与维护的总耗时,并与相同任务的基线比较;无实验记录不能给出固定提效比例。

以折扣计算函数为例:

export function calculateDiscount(price: number, couponCode?: string): number {
  if (!Number.isFinite(price) || price <= 0) throw new Error('价格必须为有限正数');
  let discountPercent = 0;
  if (couponCode === 'SAVE10') discountPercent = 10;
  else if (couponCode === 'SAVE20') discountPercent = 20;
  return price - price * (discountPercent / 100);
}

下面给出可复现的Jest示例。它们是本文整理的测试,不代表任何模型在某次实验中的原始输出:

import { calculateDiscount, isEligibleForDiscount, calculateTotal } from '../src/discount';

describe('calculateDiscount', () => {
  test('SAVE10应用10%折扣', () => expect(calculateDiscount(100, 'SAVE10')).toBe(90));
  test('SAVE20应用20%折扣', () => expect(calculateDiscount(100, 'SAVE20')).toBe(80));
  test('无优惠券时返回原价', () => expect(calculateDiscount(100)).toBe(100));
  test('未知优惠券返回原价', () => expect(calculateDiscount(100, 'INVALID')).toBe(100));
  test('处理小正值', () => expect(calculateDiscount(0.01, 'SAVE10')).toBeCloseTo(0.009, 6));
  test.each([0, -10, NaN, Infinity, -Infinity])('拒绝非法价格 %s', price => {
    expect(() => calculateDiscount(price)).toThrow('价格必须为有限正数');
  });
});

这些用例检查已约定的主流程和非法价格。实际业务仍需补查优惠券组合、状态交互、精度及并发等条件;不能从模型能生成这些用例推断它不会遗漏其他情况。

矛盾现象:高覆盖率 vs 真实质量

代码覆盖率记录测试执行了哪些语句或分支,无法单独证明断言有效。变异测试通过改变运算符、条件或常量来观察测试是否失败;存活突变体需要人工分析,可能是用例遗漏、输入域不充分,也可能与原程序行为等价,不能一概判为测试缺陷。

Stryker可对JavaScript/TypeScript项目做变异测试。下面的原函数与两种替换表达式仅用于说明突变方式,不能把三个同名函数直接拼在一个模块:

export function calculateTotal(price: number, quantity: number): number {
  return price * quantity;
}
// 突变示意1:将 return 表达式改为 price + quantity
// 突变示意2:将 return 表达式改为 price * (quantity + 1)

按Stryker定义,变异分数为检测到的有效突变体占有效突变体的比例,报告中需同时查看killed、timeout、survived和no coverage等状态,以及无效或忽略项。不同项目的分数不能直接横比;引用研究中的百分比时,还需能追溯论文页码、数据集和实验配置。

有效测试 vs 表面测试

以折扣资格函数为例:

function isEligibleForDiscount(age: number, isPremium: boolean): boolean {
  return age >= 60 || isPremium;
}

只验证单一路径的测试:

test('高级会员返回true', () => {
  expect(isEligibleForDiscount(30, true)).toBe(true);
});

该测试调用了函数,却未验证60岁边界和非会员不合格的分支;具体覆盖率应由工具测量,不能直接标成“100%覆盖率”。

补充年龄边界与独立会员资格的测试:

test('60岁及以上自动合格', () => {
  expect(isEligibleForDiscount(60, false)).toBe(true);
});
test('非高级会员未满60岁不合格', () => {
  expect(isEligibleForDiscount(59, false)).toBe(false);
});
test('高级会员资格不受年龄限制', () => {
  expect(isEligibleForDiscount(25, true)).toBe(true);
});

60岁非会员用例可区分“≥60”和“>60”;年轻会员用例可区分“或”和“且”。这些是对指定行为变化的检查,并不证明已覆盖所有突变或所有业务风险。

属性基测试:超越示例验证

属性测试(PBT)在约定输入域中生成数据,尝试寻找违反性质的反例。fast-check可配合Jest等运行器使用;运行次数是配置项,不是自动穷尽所有输入。为避免原fc.float最小边界的float32可表示性问题,本例用整数分生成价格,且限制到0.01–10000。

以下两个属性分别检查折后价范围和明确JSON数据域的往返序列化。示例固定随机种子与各1000次生成;这类范围性质较弱,仍需前面的具体折扣断言:

import fc from 'fast-check';
import { calculateDiscount } from '../src/discount';

test('property: 约定输入域内折后价为正且不超过原价', () => {
  fc.assert(fc.property(
    fc.integer({ min: 1, max: 1_000_000 }).map(cents => cents / 100),
    fc.option(fc.constantFrom('SAVE10', 'SAVE20', 'INVALID'), { nil: undefined }),
    (price, coupon) => {
      const result = calculateDiscount(price, coupon);
      expect(result).toBeGreaterThan(0);
      expect(result).toBeLessThanOrEqual(price);
    }
  ), { numRuns: 1000, seed: 830 });
});

test('property: 明确JSON输入域的往返序列化', () => {
  const value = fc.record({ name: fc.string(), count: fc.integer(), enabled: fc.boolean(), values: fc.array(fc.integer()) });
  fc.assert(fc.property(value, obj => {
    expect(JSON.parse(JSON.stringify(obj))).toEqual(obj);
  }), { numRuns: 1000, seed: 831 });
});

常见领域不变量验证包括:

  • 往返序列化:仅对所约定的JSON兼容结构检查;Date、undefined、BigInt、循环引用、NaN等不能当作任意对象无损往返。示例用字符串、整数、布尔与整数数组组成的记录。
  • 排序幂等性:多次排序结果应一致

  • 经济交易守恒:交易前后总余额保持不变

属性测试能扩大输入探索,但性质写错、输入域过窄或未连接真实实现时仍可能给出错误信心。反例应保留种子和复现路径,修正后纳入回归用例。

人机协作的未来

复现环境锁定Node.js 24.16.0、TypeScript 5.9.3、Jest 30.2.0、fast-check 4.3.0和Stryker 9.2.0。依赖以package-lock.json为准,测试只在本地隔离目录运行。可采用下列命令验证,CI接入还需项目自行配置,不是粘贴即用的发布流水线:

npm ci
npm run test:coverage
npm run test:properties
npm run test:mutation

团队仍需负责:

  • 质疑业务边缘案例是否覆盖

  • 监控覆盖率以外的质量指标

  • 审查生成测试以消除冗余

AI输出可能随模型、上下文和配置变化。应固定评估样本并重复检查,不只看通过/失败,还要看业务断言、反例质量、错误代价及测试维护成本。

结论

AI可辅助生成与整理测试,变异测试和PBT提供不同反馈,业务验收仍需人来确认。例如:

  • "用户尝试0.00元支付会发生什么?"

  • "结账过程中会话过期如何处理?"

  • "这如何影响合规性要求?"

这些源自用户体验和领域知识的质问,正是自动化测试无法替代的人类智慧。

参考与复现边界

本稿教学样例于2026年10月16日在Windows、上述锁定版本下运行:17项测试通过,两项属性各生成1000组输入;编译后discount.js的语句、分支、函数与行覆盖率均为100%。Stryker生成32个突变体,29个被检测,3个存活,报告分数90.63%;存活项涉及编译生成的模块标记,未从报告中排除。这只描述本次小样例,不能作为AI测试普遍有效或真实业务无缺陷的证明。

延伸阅读:Automated Unit Test Case Generation: A Systematic Literature Review,Jason Wang、Basem Suleiman、Muhammad Johan Alibasa,2025年4月29日提交,arXiv:2504.20357。这是一篇自动单元测试生成系统综述,本文的教学样例和运行数据不归因于该论文。

折扣函数使用浮点数演示测试思路,不规定货币舍入、税费、币种精度、可叠加优惠或退款规则,不能直接当作支付系统。极小正数也不属于本属性测试的价格域。生产金额计算需先定义最小货币单位或十进制策略并单独验证。

技术资料:自动单元测试生成系统综述;等价突变体;变异状态与分数定义;fast-check数值生成器。


​软件开发测试中的人工智能应用:在虚幻覆盖与真实质量之间