ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

用 AI 生成单元测试:从第一个测试用例开始

用 AI 生成单元测试:从第一个测试用例开始 文章目录一、什么是单元测试二、单元测试最核心的 3 个部分1. 准备测试数据2. 执行被测试的代码3. 断言结果三、为什么要让 AI 帮助生成单元测试四、先明确规则再生成测试五、完整案例为一个价格函数生成测试第一步先让 AI 列测试场景第二步检查 AI 列出的场景六、使用 Vitest 编写第一个单元测试1. 安装测试工具2. 创建被测试文件3. 创建第一个测试文件4. 执行测试七、让 AI 生成完整测试代码八、怎样检查 AI 生成的测试有没有价值1. 测试是否来自真实需求2. 测试名称是否说明行为3. 测试是否真的包含断言4. 断言是否过于宽松5. 测试是否彼此独立6. 测试是否覆盖了失败行为九、让 AI 帮你补充边界测试十、测试失败时怎样让 AI 帮你分析十一、常见的单元测试误区误区一只测试正常输入误区二测试代码和实现代码完全一样误区三为了提高覆盖率而堆测试数量误区四测试失败就修改测试误区五让 AI 一次生成整个项目的测试误区六把单元测试当成全部测试十二、单元测试和其他测试有什么区别十三、可直接复用的单元测试 Prompt 模板十四、一个适合新手的测试流程十五、总结✍创作者全栈弄潮儿 个人主页全栈弄潮儿的个人主页️ 个人社区欢迎你的加入全栈开发社区 专栏地址欢迎订阅AI 编程提效实战前一篇文章中我们学习了一个很重要的开发习惯先让 AI 出方案再让它写代码。方案确定之后还有一个问题不能忽略怎样证明写出来的代码确实符合需求很多初学者会通过手动点击页面来检查功能。手动测试当然有用但它也有一些不足每次修改代码后都需要重新操作一遍。容易漏掉边界条件。很难快速重复大量场景。代码改动后不容易确认旧功能有没有被影响。这时单元测试就可以发挥作用。单元测试并不是一开始就要写复杂的测试系统。对初学者来说可以先从一个简单函数开始准备输入 ↓ 调用函数 ↓ 得到实际结果 ↓ 和预期结果比较AI 很适合帮助我们根据需求列出测试场景。为函数生成测试代码。补充容易遗漏的边界条件。分析测试失败原因。根据修改后的代码更新测试。但测试是否真正覆盖了需求仍然需要开发者自己判断。一、什么是单元测试“单元”可以理解为程序中一个相对独立、可以单独验证的最小功能。例如一个计算价格的函数。一个验证邮箱的函数。一个格式化日期的函数。一个根据状态返回文字的函数。一个把数组转换成另一种结构的函数。单元测试就是针对这些独立功能编写测试代码确认它在不同输入下是否返回预期结果。例如有一个计算两数之和的函数functionadd(a,b){returnab;}我们可以测试输入2 和 3 预期结果5 实际结果调用 add(2, 3) 得到的结果如果实际结果等于预期结果测试通过否则测试失败。二、单元测试最核心的 3 个部分初学者先记住下面三个概念就够了准备 ↓ 执行 ↓ 断言1. 准备测试数据准备函数需要的输入。例如constprice100;constdiscount0.8;2. 执行被测试的代码调用需要验证的函数constresultcalculatePrice(price,discount);3. 断言结果判断实际结果是否符合预期expect(result).toBe(80);断言就是在告诉测试工具我希望这个结果必须是 80。如果结果不是 80测试就应该失败。三、为什么要让 AI 帮助生成单元测试单元测试最耗时的部分通常不是写一条断言而是思考哪些输入需要测试。哪些情况容易出错。边界值应该取什么。异常情况是否需要验证。多个条件组合后会不会出现问题。AI 在整理测试场景方面非常有帮助。例如我们有一个计算折扣价格的函数functioncalculatePrice(price,discount){returnprice*discount;}可以先让 AI 列出测试场景请根据下面的 JavaScript 函数设计单元测试场景。 要求 1. 列出正常输入。 2. 列出边界输入。 3. 列出异常输入。 4. 说明每个测试场景想验证什么。 5. 先不要写测试代码。 函数 function calculatePrice(price, discount) { return price * discount; }AI 可能会提醒我们考虑正常价格和正常折扣。价格为 0。折扣为 0。折扣为 1。价格为负数。折扣大于 1。参数不是数字。但这里还有一个重要问题函数到底应该怎样处理这些异常输入当前代码没有做参数校验。因此我们不能直接把 AI 列出的所有场景都写成“必须通过”的测试而要先确认业务规则。四、先明确规则再生成测试测试不是凭空产生的。它应该来自需求和业务规则。例如关于折扣价格我们可以先确定业务规则 1. price 必须是大于等于 0 的数字。 2. discount 必须是 0 到 1 之间的数字。 3. 参数不符合要求时抛出 Error。 4. 结果保留两位小数。有了规则之后AI 才能生成更准确的测试。如果不提供规则AI 可能做出不同假设把折扣0.8理解成八折。把折扣80理解成八折。允许负数价格。遇到错误输入返回 0。遇到错误输入返回null。遇到错误输入抛出异常。这些做法没有绝对的统一答案必须结合你的需求确认。五、完整案例为一个价格函数生成测试下面我们使用一个包含校验逻辑的函数exportfunctioncalculatePrice(price,discount){if(typeofprice!number||price0){thrownewError(price 必须是大于等于 0 的数字);}if(typeofdiscount!number||discount0||discount1){thrownewError(discount 必须是 0 到 1 之间的数字);}returnNumber((price*discount).toFixed(2));}这段代码的需求可以概括为正确计算价格。允许价格为 0。允许折扣为 0 和 1。拒绝负数价格。拒绝超出范围的折扣。拒绝非数字参数。最终结果保留两位小数。第一步先让 AI 列测试场景可以这样提问请为下面的 calculatePrice 函数设计单元测试场景。 业务规则 1. price 必须是大于等于 0 的数字。 2. discount 必须是 0 到 1 之间的数字。 3. 参数不符合要求时抛出 Error。 4. 结果保留两位小数。 请按照下面的分类输出 1. 正常场景。 2. 边界场景。 3. 异常场景。 每个场景需要包含 - 输入。 - 预期结果。 - 测试目的。 暂时不要生成测试代码。第二步检查 AI 列出的场景可以整理成下面的测试清单类型输入预期结果测试目的正常100, 0.880验证普通折扣计算正常99.99, 0.989.99验证小数计算和保留两位边界0, 0.80验证最低价格边界100, 00验证最低折扣边界100, 1100验证最高折扣异常-1, 0.8抛出错误拒绝负数价格异常100, -0.1抛出错误拒绝负数折扣异常100, 1.1抛出错误拒绝超过 1 的折扣异常100, 0.8抛出错误拒绝字符串价格异常100, 0.8抛出错误拒绝字符串折扣测试清单确认无误后再让 AI 生成测试代码。六、使用 Vitest 编写第一个单元测试为了让案例完整我们使用 Vitest 演示。如果你的项目使用其他测试工具核心思想基本相同主要区别是配置和断言方法的写法。1. 安装测试工具在项目根目录执行npminstall-Dvitest然后在package.json的scripts中增加测试命令{scripts:{test:vitest run}}如果项目原来已经有test命令不要直接覆盖应该结合当前项目的脚本配置进行调整。2. 创建被测试文件例如创建src/calculate-price.jsexportfunctioncalculatePrice(price,discount){if(typeofprice!number||price0){thrownewError(price 必须是大于等于 0 的数字);}if(typeofdiscount!number||discount0||discount1){thrownewError(discount 必须是 0 到 1 之间的数字);}returnNumber((price*discount).toFixed(2));}3. 创建第一个测试文件测试文件一般会和被测试文件放在相近的位置。例如创建src/calculate-price.test.jsimport{describe,expect,it}fromvitest;import{calculatePrice}from./calculate-price.js;describe(calculatePrice,(){it(正常计算八折价格,(){constresultcalculatePrice(100,0.8);expect(result).toBe(80);});});这段代码可以分成几部分理解describe把同一组测试放在一起。it描述一个具体测试场景。calculatePrice(100, 0.8)执行被测试函数。expect(result).toBe(80)判断实际结果是否等于 80。4. 执行测试在项目根目录执行npmtest如果测试通过通常会看到通过数量和执行结果。如果测试失败测试工具会显示哪个测试失败。预期结果是什么。实际结果是什么。失败发生在哪一行。测试失败并不一定说明测试代码错了也可能说明被测试的代码不符合需求。七、让 AI 生成完整测试代码第一个测试通过后可以让 AI 根据测试清单补充其他场景。推荐 Prompt请为下面的 calculatePrice 函数生成 Vitest 单元测试。 测试工具 - Vitest - JavaScript ES Module 业务规则 1. price 必须是大于等于 0 的数字。 2. discount 必须是 0 到 1 之间的数字。 3. 参数不符合要求时抛出 Error。 4. 结果保留两位小数。 必须覆盖 1. 正常价格计算。 2. 小数结果保留两位。 3. price 为 0。 4. discount 为 0。 5. discount 为 1。 6. price 为负数。 7. discount 小于 0。 8. discount 大于 1。 9. price 不是数字。 10. discount 不是数字。 要求 1. 使用 describe 和 it。 2. 每个测试只验证一个明确行为。 3. 测试名称使用中文能够说明测试目的。 4. 对抛出异常的场景使用正确的断言方式。 5. 不要修改被测试函数。 6. 生成后说明每组测试覆盖了什么。AI 生成后我们仍然要逐条阅读而不是直接复制。一个合理的测试文件可能是import{describe,expect,it}fromvitest;import{calculatePrice}from./calculate-price.js;describe(calculatePrice,(){it(正常计算八折价格,(){expect(calculatePrice(100,0.8)).toBe(80);});it(计算结果保留两位小数,(){expect(calculatePrice(99.99,0.9)).toBe(89.99);});it(价格为 0 时返回 0,(){expect(calculatePrice(0,0.8)).toBe(0);});it(折扣为 0 时返回 0,(){expect(calculatePrice(100,0)).toBe(0);});it(折扣为 1 时返回原价,(){expect(calculatePrice(100,1)).toBe(100);});it(价格为负数时抛出错误,(){expect(()calculatePrice(-1,0.8)).toThrow(price 必须是大于等于 0 的数字);});it(折扣小于 0 时抛出错误,(){expect(()calculatePrice(100,-0.1)).toThrow(discount 必须是 0 到 1 之间的数字);});it(折扣大于 1 时抛出错误,(){expect(()calculatePrice(100,1.1)).toThrow(discount 必须是 0 到 1 之间的数字);});it(价格不是数字时抛出错误,(){expect(()calculatePrice(100,0.8)).toThrow(price 必须是大于等于 0 的数字);});it(折扣不是数字时抛出错误,(){expect(()calculatePrice(100,0.8)).toThrow(discount 必须是 0 到 1 之间的数字);});});八、怎样检查 AI 生成的测试有没有价值测试文件能运行不代表测试写得好。可以从下面几个方面检查。1. 测试是否来自真实需求如果需求是空输入不能新增待办。测试就应该验证空输入时的行为而不是只测试正常输入。测试用例不是为了追求数量而是为了验证功能规则。2. 测试名称是否说明行为不推荐it(test 1,(){// ...});推荐it(输入为空时不新增待办,(){// ...});看到测试名称就应该大致知道它在保护什么行为。3. 测试是否真的包含断言下面这段测试表面上会运行但没有验证任何结果it(调用计算函数,(){calculatePrice(100,0.8);});它只是调用了函数没有判断结果是否正确。应该增加断言it(正常计算八折价格,(){constresultcalculatePrice(100,0.8);expect(result).toBe(80);});4. 断言是否过于宽松例如expect(result).toBeTruthy();这个断言只能说明结果是真值不能确认结果是不是正确的价格。更准确的断言应该是expect(result).toBe(80);断言越贴近需求测试越有价值。5. 测试是否彼此独立一个测试不应该依赖另一个测试先执行。不推荐让前一个测试修改全局数据后一个测试再依赖修改后的结果。每个测试都应该准备自己的输入执行自己的操作并验证自己的结果。6. 测试是否覆盖了失败行为很多 AI 生成的测试只覆盖“成功返回”却没有测试参数为空。参数类型错误。数据不存在。接口失败。权限不足。网络超时。测试失败行为同样重要因为真实问题通常发生在非正常流程中。九、让 AI 帮你补充边界测试当正常测试已经完成后可以单独让 AI 关注边界下面是已经存在的单元测试。 请只检查边界和异常场景是否遗漏不要修改现有测试。 需要重点检查 1. 空值。 2. 最小值和最大值。 3. 类型错误。 4. 数组为空。 5. 数组只有一项。 6. 重复数据。 7. 异步请求失败。 请输出 1. 已覆盖的边界场景。 2. 未覆盖但建议补充的场景。 3. 每个新增场景的输入和预期结果。这种提问比直接说“帮我完善测试”更容易得到聚焦的结果。十、测试失败时怎样让 AI 帮你分析测试失败后不要只发一句测试没通过怎么办应该提供完整信息我的 Vitest 测试失败了请帮我分析原因。 测试名称 正常计算八折价格 完整失败信息 [粘贴测试工具输出] 被测试函数 [粘贴相关代码] 测试代码 [粘贴失败的测试] 预期结果 80 实际结果 79.99 请按以下顺序回答 1. 解释失败信息。 2. 判断更可能是业务规则、实现代码还是测试代码的问题。 3. 给出最小排查步骤。 4. 不要直接修改代码先说明判断依据。AI 可以帮助分析但最终要结合业务规则确认。例如价格计算中的小数问题可能涉及是否要求四舍五入。是否要求截断。是否应该使用整数分保存金额。浮点数计算是否会带来误差。涉及金额时不能只看测试“通过”就认为实现一定安全。十一、常见的单元测试误区误区一只测试正常输入正常输入只能证明最顺利的路径可以运行。还需要测试边界和异常才能发现参数校验、空值处理和错误提示的问题。误区二测试代码和实现代码完全一样如果测试只是重复实现代码中的计算过程可能会把同一个错误复制一遍。测试应该表达需求和预期而不是把被测代码重新写一次。误区三为了提高覆盖率而堆测试数量覆盖率高不等于测试质量高。大量重复测试可能只是在测试相同路径没有增加实际保护。更重要的是覆盖关键业务规则和高风险场景。误区四测试失败就修改测试测试失败时不要为了让命令变绿就直接改预期结果。先确认需求是否发生变化。实现代码是否有问题。测试输入是否合理。预期结果是否符合业务规则。误区五让 AI 一次生成整个项目的测试一次生成大量测试可能带来测试依赖不存在的文件。测试框架配置不匹配。测试使用了项目没有的接口。断言和真实业务不一致。出错后很难判断是哪一组测试有问题。更适合的方式是从一个函数、一组规则开始逐步增加测试。误区六把单元测试当成全部测试单元测试主要验证独立函数或模块。它不能完全替代页面交互测试。接口联调测试。数据库测试。端到端测试。性能测试。安全测试。不同类型的测试负责发现不同问题。十二、单元测试和其他测试有什么区别初学者可以先用下面的方式理解测试类型主要验证什么示例单元测试一个函数或小模块计算折扣是否正确接口测试接口输入和返回结果搜索接口是否返回正确商品组件测试一个页面组件的行为点击按钮后是否显示列表端到端测试用户完整操作流程登录后进入首页性能测试系统在压力下的表现大量请求时响应时间AI 可以帮助设计这些测试但第一步仍然建议从最小的单元测试开始。十三、可直接复用的单元测试 Prompt 模板下面是一份可以直接使用的模板请为下面的代码生成单元测试。 项目环境 - 编程语言[例如 JavaScript] - 测试框架[例如 Vitest] - 模块格式[例如 ES Module] 被测试代码 [粘贴函数或模块代码] 业务需求 [说明这段代码应该实现什么] 已知规则 - [规则 1] - [规则 2] - [规则 3] 请先输出测试场景清单不要立即写代码。 测试场景需要覆盖 1. 正常输入。 2. 边界输入。 3. 异常输入。 4. 空值和类型错误。 5. 依赖失败或返回异常。 确认场景后再生成测试代码。 代码要求 1. 使用当前项目已有的测试框架。 2. 每个测试只验证一个明确行为。 3. 测试名称清楚描述预期行为。 4. 每个测试必须包含有效断言。 5. 不修改被测试代码。 6. 说明每组测试覆盖了哪些业务规则。 7. 如果信息不足请先列出需要确认的问题。如果已经有一组测试还可以使用这个模板请审查下面的单元测试不要直接重写。 请检查 1. 是否覆盖业务需求。 2. 是否缺少边界和异常场景。 3. 断言是否准确。 4. 测试之间是否相互独立。 5. 是否存在重复或没有价值的测试。 6. 是否依赖了不稳定的时间、随机数或外部服务。 请先输出问题清单和修改建议 再给出需要新增或修改的最小测试代码。十四、一个适合新手的测试流程以后为一个新函数写测试时可以按照下面的流程明确业务规则 ↓ 让 AI 列出测试场景 ↓ 自己确认场景和预期 ↓ 让 AI 生成最小测试代码 ↓ 运行测试 ↓ 分析失败原因 ↓ 补充边界和异常测试每一步都要有自己的判断。不要一开始追求写出几十个测试先保证最重要的规则被准确验证。十五、总结单元测试的入门并不复杂核心就是给定输入 ↓ 调用代码 ↓ 验证预期结果使用 AI 生成单元测试时要注意先明确业务规则再让 AI 设计测试。先列测试场景再生成测试代码。正常、边界和异常情况都要考虑。每个测试都应该包含清晰、准确的断言。测试文件能运行不代表测试真的有价值。测试失败后要结合需求判断是实现、测试还是规则出了问题。单元测试不能替代接口、页面、性能和安全测试。可以把今天的内容浓缩成一句话AI 可以帮你写测试代码但你必须负责确认测试到底应该证明什么。当你开始为代码补充测试时AI 的作用就不只是生成实现代码也能帮助你更早发现问题、保护已有功能。下一篇文章我们将继续学习《用 AI 做代码优化哪些建议值得采纳》✍坚持原创求关注点赞收藏
返回列表