当前位置: 首页 > news >正文

agent-skills中的测试驱动开发:如何让AI代理写出可靠代码

agent-skills中的测试驱动开发:如何让AI代理写出可靠代码

【免费下载链接】agent-skillsProduction-grade engineering skills for AI coding agents.项目地址: https://gitcode.com/gh_mirrors/agentskill/agent-skills

在AI驱动的开发环境中,确保代码质量和可靠性变得前所未有的重要。agent-skills项目中的测试驱动开发(TDD)技能为AI代理提供了一套系统化方法,通过先写测试再编码的方式,确保产出的代码既可靠又易于维护。本文将详细介绍如何利用agent-skills中的测试驱动开发技能,让AI代理遵循专业工程实践,构建高质量软件。

什么是测试驱动开发(TDD)?

测试驱动开发是一种软件开发方法论,其核心原则是在编写实际功能代码前先编写失败的测试。这种"测试先行"的方式迫使开发者在动手编码前先明确需求和预期行为,从而减少后期修改成本并提高代码质量。

agent-skills中的test-driven-development技能将这一方法论系统化,为AI代理提供了清晰的工作流程和最佳实践。当需要实现新功能、修复bug或修改现有行为时,TDD技能能指导AI代理构建可靠的解决方案。

TDD的核心工作流程:红-绿-重构循环

agent-skills的TDD技能基于经典的"红-绿-重构"循环,这三个阶段形成了一个持续改进的开发闭环:

1. 红(RED):编写失败的测试

首先编写一个必然失败的测试。这个测试定义了期望的功能或行为,但由于实现代码尚未编写,测试应该明确地失败。

// RED: 这个测试会失败,因为createTask函数还不存在 describe('TaskService', () => { it('创建带有标题和默认状态的任务', async () => { const task = await taskService.createTask({ title: '购买 groceries' }); expect(task.id).toBeDefined(); expect(task.title).toBe('购买 groceries'); expect(task.status).toBe('pending'); expect(task.createdAt).toBeInstanceOf(Date); }); });

一个立即通过的测试是没有价值的,它无法证明代码的正确性。只有先看到测试失败,才能确认当测试通过时,确实是因为功能被正确实现。

2. 绿(GREEN):编写最小化代码

在测试失败后,编写刚好足够的代码使测试通过。这一阶段不追求代码的完美,只关注让测试通过。

// GREEN: 最小化实现 export async function createTask(input: { title: string }): Promise<Task> { const task = { id: generateId(), title: input.title, status: 'pending' as const, createdAt: new Date(), }; await db.tasks.insert(task); return task; }

这个阶段的目标是快速获得反馈,验证测试的有效性,并确认基本功能可以工作。

3. 重构(REFACTOR):优化代码

测试通过后,进入重构阶段。在保持测试通过的前提下,改进代码质量:

  • 提取重复逻辑
  • 优化命名
  • 消除代码异味
  • 提高性能

每次重构后都要运行测试,确保功能没有被破坏。这个循环不断重复,逐步构建出高质量的代码。

修复bug的"证明它"模式(Prove-It Pattern)

agent-skills的TDD技能特别强调了修复bug时的"证明它"模式,这是一种确保bug被彻底修复且不会复发的有效方法:

  1. 收到bug报告
  2. 编写能复现bug的测试(此时测试应该失败)
  3. 实现修复
  4. 验证测试通过
  5. 运行完整测试套件确保没有回归
// 步骤1: 编写复现测试(应该失败) it('完成任务时设置completedAt时间戳', async () => { const task = await taskService.createTask({ title: '测试' }); const completed = await taskService.completeTask(task.id); expect(completed.status).toBe('completed'); expect(completed.completedAt).toBeInstanceOf(Date); // 这会失败 → 确认bug存在 }); // 步骤2: 修复bug export async function completeTask(id: string): Promise<Task> { return db.tasks.update(id, { status: 'completed', completedAt: new Date(), // 之前缺少这行 }); } // 步骤3: 测试通过 → bug修复,防止回归

这种方法确保每个bug修复都有对应的测试保护,避免未来的修改再次引入相同问题。

测试金字塔:平衡测试策略

agent-skills的TDD技能倡导遵循测试金字塔原则,合理分配测试资源:

╱╲ ╱ ╲ 端到端测试 (~5%) ╱ ╲ 完整用户流程,真实浏览器环境 ╱──────╲ ╱ ╲ 集成测试 (~15%) ╱ ╲ 组件交互,API边界测试 ╱────────────╲ ╱ ╲ 单元测试 (~80%) ╱ ╲ 纯逻辑,隔离测试,毫秒级执行 ╱──────────────────╲
  • 单元测试(约80%):测试独立功能单元,快速且隔离
  • 集成测试(约15%):测试组件间交互和API边界
  • 端到端测试(约5%):测试关键用户流程,使用真实浏览器环境

遵循"碧昂斯规则":如果你喜欢它,就应该为它写个测试。基础设施变更、重构和迁移不负责捕获bug,测试才是。如果一个变更破坏了代码而你没有对应的测试,那是你的责任。

编写高质量测试的关键原则

agent-skills的TDD技能提供了编写有效测试的详细指南,以下是核心原则:

测试状态而非交互

断言操作的结果,而非内部方法调用。验证方法调用序列的测试在重构时容易断裂,即使行为未变。

// 好:测试函数做什么(基于状态) it('按创建日期降序返回任务', async () => { const tasks = await listTasks({ sortBy: 'createdAt', sortOrder: 'desc' }); expect(tasks[0].createdAt.getTime()) .toBeGreaterThan(tasks[1].createdAt.getTime()); }); // 差:测试函数内部如何工作(基于交互) it('调用db.query时使用ORDER BY created_at DESC', async () => { await listTasks({ sortBy: 'createdAt', sortOrder: 'desc' }); expect(db.query).toHaveBeenCalledWith( expect.stringContaining('ORDER BY created_at DESC') ); });

DAMP优于DRY

在生产代码中,DRY(不重复自己)通常是对的。但在测试中,DAMP(描述性和有意义的短语)更好。测试应该读起来像规格说明,每个测试都应该讲述完整的故事,不需要读者追踪共享辅助函数。

优先使用真实实现而非模拟

使用最简单的测试替身完成工作。测试使用真实代码越多,提供的信心越足:

优先顺序(从最优先到最不优先): 1. 真实实现 → 最高信心,捕获真实bug 2. 伪实现 → 依赖的内存版本(如假数据库) 3. 存根 → 返回预设数据,无行为 4. 模拟(交互) → 验证方法调用 — 谨慎使用

只有当真实实现太慢、不确定或有无法控制的副作用(外部API、发送邮件)时才使用模拟。过度模拟会导致测试通过但生产环境失败。

采用Arrange-Act-Assert模式

it('截止日期过后标记任务为逾期', () => { // Arrange: 设置测试场景 const task = createTask({ title: '测试', deadline: new Date('2025-01-01'), }); // Act: 执行被测试的操作 const result = checkOverdue(task, new Date('2025-01-02')); // Assert: 验证结果 expect(result.isOverdue).toBe(true); });

每个概念一个断言

// 好:每个测试验证一个行为 it('拒绝空标题', () => { ... }); it('从标题中修剪空白', () => { ... }); it('强制执行标题最大长度', () => { ... }); // 差:所有内容放在一个测试中 it('正确验证标题', () => { expect(() => createTask({ title: '' })).toThrow(); expect(createTask({ title: ' hello ' }).title).toBe('hello'); expect(() => createTask({ title: 'a'.repeat(256) })).toThrow(); });

描述性的测试命名

// 好:读起来像规格说明 describe('TaskService.completeTask', () => { it('设置状态为已完成并记录时间戳', ...); it('对不存在的任务抛出NotFoundError', ...); it('是幂等的 — 完成已完成的任务是无操作', ...); it('向任务负责人发送通知', ...); }); // 差:模糊的名称 describe('TaskService', () => { it('works', ...); it('处理错误', ...); it('test 3', ...); });

常见的测试反模式及避免方法

agent-skills的TDD技能详细列出了需要避免的测试反模式:

反模式问题解决方案
测试实现细节重构时即使行为未变,测试也会断裂测试输入和输出,而非内部结构
不稳定测试(时序、顺序依赖)削弱对测试套件的信任使用确定性断言,隔离测试状态
测试框架代码浪费时间测试第三方行为只测试你的代码
滥用快照大型快照无人审查,任何更改都会断裂谨慎使用快照并审查每个更改
无测试隔离单独运行通过但一起运行失败每个测试设置和清理自己的状态
模拟一切测试通过但生产环境失败优先使用真实实现 > 伪实现 > 存根 > 模拟。只在真实依赖慢或不确定时模拟

何时在agent-skills中使用子代理进行测试

对于复杂的bug修复,agent-skills建议生成子代理来编写复现测试:

主代理:"生成一个子代理来编写复现此bug的测试: [bug描述]。使用当前代码时测试应该失败。" 子代理:编写复现测试 主代理:验证测试失败,然后实现修复, 然后验证测试通过。

这种分离确保测试是在不知道修复方案的情况下编写的,使其更加健壮。

开始使用agent-skills中的TDD技能

要在项目中使用agent-skills的测试驱动开发能力,请参考以下资源:

  • 官方TDD技能文档:skills/test-driven-development/SKILL.md
  • 测试模式参考:references/testing-patterns.md
  • 浏览器测试指南:skills/browser-testing-with-devtools/SKILL.md

开始使用TDD的最佳方式是:

  1. 克隆agent-skills仓库:git clone https://gitcode.com/gh_mirrors/agentskill/agent-skills
  2. 阅读docs/getting-started.md中的设置说明
  3. 在给AI代理的指令中明确引用TDD技能:"遵循test-driven-development流程进行此更改"

克服常见的TDD阻力

采用TDD时,开发者常提出一些理由反对,agent-skills的TDD技能对这些理由进行了有力反驳:

理由现实
"我会在代码工作后写测试"你不会。事后写的测试测试的是实现,而不是行为。
"这太简单了,不需要测试"简单代码会变得复杂。测试记录了预期行为。
"测试拖慢了我"测试现在拖慢你,但以后每次更改代码时都会加速你。
"我手动测试过了"手动测试不会持久。明天的更改可能会破坏它,而你无从知晓。
"代码自解释"测试才是规格说明。它们记录代码应该做什么,而不是它做了什么。
"这只是个原型"原型会变成生产代码。从第一天开始写测试可以防止"测试债务"危机。

结论:用TDD构建更可靠的AI生成代码

agent-skills中的测试驱动开发技能为AI代理提供了一套系统化方法,确保生成的代码既可靠又易于维护。通过"红-绿-重构"循环、"证明它"bug修复模式和测试金字塔策略,AI代理能够遵循专业工程实践,构建高质量软件。

无论是实现新功能、修复bug还是优化现有代码,TDD都能帮助AI代理产出更健壮、更可维护的代码。开始使用test-driven-development技能,体验测试先行开发带来的好处!

【免费下载链接】agent-skillsProduction-grade engineering skills for AI coding agents.项目地址: https://gitcode.com/gh_mirrors/agentskill/agent-skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.jsqmd.com/news/753048/

相关文章:

  • 淘系风控tfstk分析
  • 从CNVD已公开漏洞报告里“淘金”:手把手教你复现并深挖关联漏洞,一份报告变多张证书
  • flutter中 onGenerateRoute回调函数
  • 企业如何利用Taotoken实现多团队API密钥管理与访问审计
  • Kotlin 数据容器 - Array sort 系列方法与 drop 系列方法
  • 3个痛点,1个解决方案:Transmission Remote GUI让你的远程BT下载管理如此简单
  • 为什么同一篇论文知网和维普AI率差这么多:两平台检测原理差异深度解读
  • Spring Boot 3 JWT Security测试指南:如何编写完整的认证授权测试用例
  • 3分钟快速掌握WindowResizer:Windows窗口强制调整大小的终极技巧
  • Taotoken 模型广场如何帮助开发者快速选型与切换
  • 终极指南:掌握Vosk离线语音识别API的7个实战技巧与性能优化方案
  • 使用Taotoken CLI工具一键配置团队统一的模型调用环境
  • 从Startup.cs到零配置启动:.NET 9中Minimal Hosting + Configuration DSL的5步迁移路径
  • 模块化重构倒计时:C++23项目升级C++27模块的最后90天行动纲领(含自动化转换脚本v2.7.1)
  • 别再只盯着CIoU了!实测YOLOv5换上Wise-IoU v1,我的缺陷检测mAP涨了快10个点
  • GBFR Logs完全解析:碧蓝幻想Relink玩家的游戏数据分析与性能监控终极指南
  • Fish Speech-1.5开源模型实战:为Rust/Go服务提供gRPC语音合成接口
  • Translumo终极指南:免费实时屏幕翻译工具快速上手教程
  • STM32按键去抖防竞争方案
  • 别再手动盖油了!用AD20设计规则搞定过孔盖油,一劳永逸不出错
  • 观察 Taotoken 在多模型聚合调用下的路由与容灾效果
  • ExtractorSharp:5分钟掌握专业级游戏资源编辑器完整指南 [特殊字符]
  • 使用 Python 快速接入 Taotoken 并调用多模型完成聊天补全任务
  • 拆解 Warp AI Agent(四):增量知识引擎——Merkle Tree 如何让代码索引降到 O(changes)
  • JsRpc快速上手:5分钟搭建远程浏览器执行环境
  • 为什么降AI工具改写后文章更难读:改写质量和可读性权衡免费解决方案深度解读
  • 将Taotoken作为统一入口整合企业内多个AI应用场景
  • 对比自建代理与使用Taotoken聚合服务在运维复杂度上的差异
  • 别再傻傻遍历了!用Python的binascii.crc32高效破解短数据(避坑指南)
  • linux内核 虚拟地址空间如何组织