news 2026/9/15 19:19:46

Codebuff SDK 端到端测试实战指南:E2E、集成测试与可运行示例全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Codebuff SDK 端到端测试实战指南:E2E、集成测试与可运行示例全解析

Codebuff SDK 端到端测试实战指南:E2E、集成测试与可运行示例全解析

【免费下载链接】freebuffThe free coding agent项目地址: https://gitcode.com/GitHub_Trending/cod/freebuff

Codebuff SDK(@codebuff/sdk)为开发者提供了调用 Codebuff AI 编码 Agent 的完整运行时能力。本文围绕仓库中sdk/e2e目录的测试体系展开,系统讲解其四层测试结构(端到端测试、集成测试、单元测试、可运行示例)、环境变量与运行方式、测试工具设施(EventCollector、Mock 机制、测试夹具)以及标准的测试编写模式。读完本文,你将能够直接运行这套测试套件、理解流式事件与子 Agent 流的验证方法,并掌握编写属于自己的 SDK E2E 测试的完整套路。

一、sdk/e2e目录结构与测试分层

sdk/e2e目录是 SDK 的测试与示例集散地,核心文档为 sdk/e2e/README.md。整个目录按职责划分为七个子目录:

sdk/e2e/ ├── streaming/ # E2E 测试:流式行为(子 Agent 流、并发流) ├── workflows/ # E2E 测试:多步骤工作流(多轮对话、错误恢复) ├── custom-agents/ # E2E 测试:自定义 Agent 与自定义工具集成 ├── features/ # E2E 测试:SDK 特性覆盖(projectFiles、knowledgeFiles、maxAgentSteps) ├── integration/ # 集成测试(SDK + API 机制) ├── examples/ # 可运行示例脚本(不是测试) └── utils/ # 共享工具 ├── __tests__/ # 工具类的单元测试 ├── event-collector.ts ├── get-api-key.ts └── test-fixtures.ts

这种分层设计遵循了经典测试金字塔思路:E2E 测试验证完整用户工作流(真实或模拟 API 调用),集成测试验证 SDK 与后端 API 之间的机制契约(事件类型、事件顺序、流式分块、连接检测),单元测试则纯本地、无外部依赖地验证工具类本身。

测试脚本定义在 sdk/package.json 中,四个核心命令分别为:

命令实际执行内容
test:e2ebun test e2e/streaming/ e2e/workflows/ e2e/custom-agents/ e2e/features/
test:integrationbun test e2e/integration/
test:unit:e2e通过test:files汇总srce2e/utils下的*.test.ts(排除*.integration.test.ts
testbun test $(bun run --silent test:files)

从脚本可以看出,E2E 与集成测试全部基于 Bun 的测试运行器(bun:test),这也是所有测试文件统一使用import { describe, test, expect, beforeAll } from 'bun:test'的原因。

二、前置条件:环境变量与 Mock 模式

根据 sdk/e2e/README.md 与 sdk/e2e/utils/get-api-key.ts,运行测试需要以下前置条件:

  • API Key:E2E 与集成测试需要设置CODEBUFF_API_KEY环境变量;
  • Opt-in 开关:本地进行真实 API 调用需设置RUN_CODEBUFF_E2E=true(CI 环境默认自动执行);
  • 优雅跳过:文档说明未设置 API Key 时测试会优雅跳过。

深入源码会发现一套更有意思的实现机制:getApiKey()并不是简单地抛错或跳过,而是区分两种模式:

// sdk/e2e/utils/get-api-key.ts(要点) const shouldRunLiveE2e = process.env.RUN_CODEBUFF_E2E === 'true' export function getApiKey(): string { if (shouldRunLiveE2e) { const apiKey = process.env.CODEBUFF_API_KEY if (!apiKey) { throw new Error('CODEBUFF_API_KEY environment variable is required for live e2e tests. ...') } return apiKey } setupE2eMocks() // 注入全部 Mock process.env.CODEBUFF_API_KEY = E2E_MOCK_API_KEY return E2E_MOCK_API_KEY // 'codebuff-e2e-mock' }

也就是说,未设置RUN_CODEBUFF_E2E=true时,测试并不会真的打到线上,而是自动进入 Mock 模式:注入一个固定的模拟 Key(codebuff-e2e-mock),保证测试在本地可确定性运行。skipIfNoApiKey()的实现恒返回false,因此所有测试用例始终执行,只是底层从"真实调用"切换为"模拟调用"。

Mock 机制底层实现

Mock 的核心实现在 sdk/e2e/utils/e2e-mocks.ts,它使用 Bun 测试运行器内置的spyOn对 SDK 的数据库层与 LLM 层进行替换:

  • 数据库模块sdk/src/impl/database):替换getUserInfoFromApiKeyfetchAgentFromDatabasestartAgentRunfinishAgentRunaddAgentStep,其中 Agent 模板由buildMockAgentTemplatepublisherId/agentId@version的 ID 格式动态构造;
  • LLM 模块sdk/src/impl/llm):替换promptAiSdkStreampromptAiSdkpromptAiSdkStructured,模拟流式输出与结构化输出;
  • 连接检测:将CodebuffClient.prototype.checkConnection直接 Mock 为恒返回true

模拟 LLM 层还内置了一套"关键词响应路由"(buildMockResponseText):例如提示词包含weather会返回The weather is sunny, temperature 72F.,包含favorite number is会返回Got it.,包含2 + 2会返回4。工具调用则由buildMockToolCall按提示词关键词自动选择get_weatherexecute_sqlapply_patchfetch_api等工具并生成输入。这正是多轮对话、天气 Agent 等 E2E 测试能够在本地稳定断言的根基。

错误识别辅助

sdk/e2e/utils/get-api-key.ts 还提供两个断言辅助函数:

  • isAuthError(output):当输出类型为error且消息包含authenticationapi keyunauthorized时判定为鉴权错误;
  • isNetworkError(output):当状态码为 408(超时)、429(限流)或 5xx,或消息包含network error时判定为网络错误。

二者共同帮助测试区分"鉴权失败"与"网络抖动",在 CI 中便于对偶发错误做降级处理。

三、E2E 测试详解(test:e2e

E2E 测试通过bun run test:e2e运行,覆盖四类场景,对应四个子目录:

目录覆盖场景对应测试文件
streaming/子 Agent 流式输出、并发流隔离subagent-streaming.e2e.test.ts、concurrent-streams.e2e.test.ts
workflows/多轮对话、错误恢复multi-turn-conversation.e2e.test.ts、error-recovery.e2e.test.ts
custom-agents/自定义 Agent + 自定义工具weather-agent.e2e.test.ts、database-query-agent.e2e.test.ts、api-integration-agent.e2e.test.ts、apply-patch-tool.e2e.test.ts
features/projectFiles、knowledgeFiles、maxAgentStepsproject-files.e2e.test.ts、knowledge-files.e2e.test.ts、max-agent-steps.e2e.test.ts

3.1 流式行为:子 Agent 流与并发流

子 Agent 流测试(subagent-streaming.e2e.test.ts)验证嵌套子 Agent 的事件流与父子关系,核心断言包括:

  • subagent_startsubagent_finish事件按agentId成对出现;
  • subagent_start事件的agentIdagentTypedisplayName均为字符串,onlyChild为布尔值,parentAgentIdprompt为可选的同类型字段;
  • 子 Agent 的文本块通过handleStreamChunksubagent_chunk结构转发,包含agentIdagentTypechunk字段;
  • 同一agentId不应出现重复的subagent_start事件。

测试中使用的 Agent 是codebuff/base@latest,其特点是会在执行中派生文件选择、代码搜索等子 Agent,从而天然触发上述事件。

并发流测试(concurrent-streams.e2e.test.ts)则验证多个并发client.run()之间的事件流互不干扰:为每个 run 单独创建EventCollector,通过Promise.all同时发起 2~3 个 run,断言每个收集器都有独立的start/finish事件、独立的streamChunks,且错误列表为空。该测试在工程上很有价值——它证明了 SDK 客户端在多 run 场景下的流隔离能力。

3.2 多轮对话:previousRun链式续聊

多轮对话测试(multi-turn-conversation.e2e.test.ts)验证previousRun机制:第一次 run 返回的RunState作为下一次run()previousRun传入,从而保持上下文。

测试用三段式对话验证记忆保持:先让 Agent 记住"最喜欢的数字是 42",第二轮问"我告诉你的最喜欢的数字是什么",断言collector2.getFullText()中包含42;随后用 todo app 场景做三连击,第三轮断言回复中包含todotask。从源码看,previousRun正是 sdk/src/run.ts 中RunOptions的字段,其注释明确指出:传入上一次run()返回的 JSON 状态即可延续会话上下文。

3.3 错误恢复与中止

错误恢复测试(error-recovery.e2e.test.ts)覆盖四种异常场景:

  • 空 prompt:不应崩溃,至少产生start事件;
  • 不存在的 Agentnonexistent-agent-that-does-not-exist-12345,输出要么是带messageerror,要么被优雅处理;
  • 特殊字符 prompt:emoji、引号、反引号、换行混排的 prompt 应正常完成并产生finish
  • AbortController 中止signal: abortController.signal在 500ms 后中止长任务,断言结果为 error 或已有部分事件。

其中signal参数在 sdk/src/run.ts 的RunOptions中有明确定义,说明 SDK 原生支持AbortSignal取消机制。

3.4 自定义 Agent 与自定义工具

自定义 Agent 测试演示了 SDK 最强大的扩展能力:通过agentDefinitionscustomToolDefinitions注入自定义 Agent 和工具。以天气 Agent(weather-agent.e2e.test.ts)为例:

const weatherAgent: AgentDefinition = { id: 'weather-agent', model: 'anthropic/claude-sonnet-4.5', displayName: 'Weather Agent', toolNames: ['get_weather'], instructionsPrompt: `You are a helpful weather assistant. When asked about weather, use the get_weather tool to fetch current conditions.`, } const weatherTool = getCustomToolDefinition({ toolName: 'get_weather', description: 'Get current weather for a city', inputSchema: z.object({ city: z.string().describe('Name of the city') }), exampleInputs: [{ city: 'New York' }], execute: async ({ city }) => { const weather = MOCK_WEATHER_DATA[city] || { temp: 65, condition: 'Unknown' } return [{ type: 'json', value: { city, temperature: weather.temp, condition: weather.condition } }] }, })

随后通过client.run({ agent: 'weather-agent', agentDefinitions: [weatherAgent], customToolDefinitions: [weatherTool], ... })执行,断言产生了tool_call事件(toolName === 'get_weather'),且回复包含天气相关信息。

这里的getCustomToolDefinition实现在 sdk/src/custom-tool.ts,其参数含义如下:

参数类型说明
toolNamestring工具名称;若与内置工具同名会得到类型层面的报错提示,要求改用overrideTools
inputSchemaz.ZodTypeZod 4 输入校验 schema,LLM 调用工具时按此校验参数
descriptionstring传给 LLM 的工具描述,说明工具做什么、何时使用
endsAgentStepboolean默认true,表示该工具调用会作为一步的终止序列,LLM 必须等待工具结果后才能继续调用其他工具
exampleInputsInput[]示例输入,用于帮助 LLM 理解调用格式
execute(params) => Promise<ToolResultOutput[]>工具执行体,返回ToolResultOutput数组(如{ type: 'json', value }

配套的数据库查询 Agent(database-query-agent.e2e.test.ts)展示了execute_sql工具与MOCK_DATABASE夹具(users 表含 Alice/Bob/Charlie)的组合用法,并实现了简单的 WHERE 子句解析;API 集成 Agent(api-integration-agent.e2e.test.ts)展示了fetch_api工具,对jsonplaceholder/example域名返回 Mock 数据、对其他 URL 走真实 fetch(带 5 秒 AbortController 超时);apply_patch 测试(apply-patch-tool.e2e.test.ts)则在临时目录中真实执行文件补丁并回读验证文件内容。

3.5 SDK 特性覆盖

  • projectFiles(project-files.e2e.test.ts):将项目文件以{ 'src/index.ts': '...' }形式注入,Agent 可据此列出文件、分析内容。夹具定义在 sdk/e2e/utils/test-fixtures.ts 的SAMPLE_PROJECT_FILES中;
  • knowledgeFiles(knowledge-files.e2e.test.ts):注入"知识文件",Agent 能从中读取秘密暗号(PINEAPPLE42)或公司价值观(Innovation / Integrity)。与projectFiles的区别在 sdk/src/client.ts 的注释中有明确说明:knowledgeFiles会直接加入 Agent 上下文,而projectFiles用于帮助 Codebuff 挑选合适的源码文件作为上下文;
  • maxAgentSteps(max-agent-steps.e2e.test.ts):限制 Agent 最大执行步数(如 5、2),作为防止 Agent 失控的安全阀。sdk/src/client.ts 的文档注释建议一个合理的默认值约为 20。

四、集成测试详解(test:integration

集成测试通过bun run test:integration运行,聚焦 SDK 与 API 的机制层契约,目录为sdk/e2e/integration/

测试文件验证内容
event-types.integration.test.ts验证所有PrintModeEvent类型均被正确发射
event-ordering.integration.test.ts验证事件顺序:start → content → finish
stream-chunks.integration.test.ts验证handleStreamChunk回调
connection-check.integration.test.ts验证checkConnection()方法

4.1 事件类型契约

事件类型测试断言了PrintModeEvent的核心结构(类型定义见 common/src/types/print-mode.ts):

  • start:run 开始时发射,携带messageHistoryLength(number);
  • finish:run 结束时发射,携带totalCost(number,非负);
  • text:响应生成过程中的文本事件,多个text事件拼接即为完整回复;
  • tool_call/tool_result:工具调用时成对出现,分别携带toolCallIdtoolNameinputtoolCallIdtoolNameoutput
  • 此外还有errorsubagent_startsubagent_finishreasoning_deltadownload等类型。

完整的PrintModeEvent判别联合(discriminated union)由 Zod 的printModeEventSchema定义,SDK 在 sdk/src/index.ts 中将其作为公共类型导出。

4.2 事件顺序契约

事件顺序测试(event-ordering.integration.test.ts)是"流式渲染正确性"的基石,验证了五条规则:

  1. start事件必须是第一个事件(findIndex结果为 0);
  2. finish事件必须晚于所有text事件;
  3. 同一toolCallIdtool_result必须晚于其tool_call
  4. 标准流程start → text → finish可用collector.verifyEventOrder(['start', 'finish'])验证;
  5. 最后一个finish之后不允许再出现任何非 finish 事件。

这些断言直接支撑了 UI 层"先渲染开始状态、流式追加文本、最终收尾"的渲染逻辑。

4.3 流式分块契约

流式分块测试(stream-chunks.integration.test.ts)验证handleStreamChunk的三种块类型:

  • 字符串块:文本流式输出,多个块拼接成完整文本;
  • subagent_chunk:子 Agent 的文本块,含agentIdagentTypechunk
  • reasoning_chunk:推理过程块,含agentIdancestorRunIdschunk(类型定义同样在 sdk/src/run.ts 的CodebuffClientOptions中)。

测试还验证了块是增量到达的(记录每个块到达时间戳,长响应应有多个块),以及空 prompt、超长响应、特殊字符(emoji、引号、换行、制表符)场景下的流式健壮性。

4.4 连接检测

连接检测测试(connection-check.integration.test.ts)验证checkConnection()在后台可达时返回true且返回值为布尔类型。其实现位于 sdk/src/client.ts,通过请求/api/healthz并校验status === 'ok'实现,带 5 秒超时;BYOK 模式下则改为探测 OpenRouter 的keymodels端点。

五、单元测试:EventCollector(test:unit:e2e

单元测试目录为sdk/e2e/utils/__tests__/,目前包含 event-collector.test.ts,纯本地运行、无任何外部依赖。测试覆盖EventCollector的全部 API:

  • handleEvent:按序收集事件,并单独追踪error事件;
  • handleStreamChunk:收集字符串块与subagent_chunk
  • getEventsByType/hasEventType/getFirstEvent/getLastEvent:按类型查询、判断、取首取尾;
  • getFullText/getFullStreamText:拼接全部文本事件 / 拼接字符串流块;
  • getSubagentChunks(agentId):按 Agent 过滤子 Agent 流块;
  • verifyEventOrder:按期望顺序校验事件出现次序(支持部分顺序);
  • getUniqueEventTypes/countEvents/clear/getSummary:去重、计数、清空与调试摘要。

EventCollector本身实现在 sdk/e2e/utils/event-collector.ts,是整个测试套件的"断言中枢"——几乎所有 E2E 与集成测试都通过它收集事件再断言。其StreamChunk类型(string | subagent_chunk | reasoning_chunk)与 sdk/src/run.ts 中的回调签名完全对齐。

六、可运行示例:开箱即用的 SDK 用法

examples/目录包含 6 个可运行脚本(非测试),覆盖了 SDK 最常见的六类工程场景。运行方式为:

bun run sdk/e2e/examples/code-reviewer.example.ts
示例功能描述关键点
code-reviewer.example.tsAI 代码审查提交含除零 Bug 的divide函数,让 Agent 找出问题
code-explainer.example.ts用通俗语言解释代码针对fetchUserData异步函数
commit-message-generator.example.ts根据 diff 生成提交信息传入 git diff 文本
sdk-lint.example.tsAI 驱动的 Linter让 Agent 以 linter 身份给出具体反馈
sdk-refactor.example.ts代码重构要求使用现代 JavaScript 特性重写
sdk-test-gen.example.ts单元测试生成要求生成 Jest 测试

所有示例的结构高度一致,是最佳实践模板:

  1. 读取CODEBUFF_API_KEY环境变量,缺失即报错退出;
  2. 构造new CodebuffClient({ apiKey })
  3. 调用client.run({ agent: 'codebuff/base2@latest', prompt, handleStreamChunk })
  4. 通过handleStreamChunk将字符串块实时写入process.stdout实现流式打印;
  5. 检查result.output.type === 'error'处理失败。

其中handleStreamChunk只对typeof chunk === 'string'的块做输出、忽略subagent_chunk等结构化块,是流式 UI 的极简范例。

七、标准测试编写模式

sdk/e2e/README.md 给出了两类官方推荐的测试模板,可直接复用。

7.1 E2E 测试模板

import { describe, test, expect, beforeAll } from 'bun:test' import { CodebuffClient } from '../../src/client' import { EventCollector, getApiKey, skipIfNoApiKey, isAuthError, DEFAULT_AGENT, DEFAULT_TIMEOUT } from '../utils' describe('E2E: My Test', () => { let client: CodebuffClient beforeAll(() => { if (skipIfNoApiKey()) return client = new CodebuffClient({ apiKey: getApiKey() }) }) test('does something', async () => { if (skipIfNoApiKey()) return const collector = new EventCollector() const result = await client.run({ agent: DEFAULT_AGENT, prompt: 'Test prompt', handleEvent: collector.handleEvent, }) if (isAuthError(result.output)) return expect(result.output.type).not.toBe('error') }, DEFAULT_TIMEOUT) })

模板要素拆解:

  • beforeAll+skipIfNoApiKey():集中初始化客户端,保持用例简洁;
  • getApiKey():自动处理真实 Key 与 Mock Key 的切换;
  • EventCollector:统一收集事件用于断言;
  • isAuthError(result.output):鉴权错误时直接返回,避免 CI 误报;
  • DEFAULT_TIMEOUT:超时上限为 120 秒(2 分钟,定义于 sdk/e2e/utils/test-fixtures.ts),复杂场景如子 Agent 流会翻倍使用(DEFAULT_TIMEOUT * 2);
  • DEFAULT_AGENT:默认 Agent 为base2

7.2 单元测试模板

import { describe, test, expect, beforeEach } from 'bun:test' import { EventCollector } from '../event-collector' describe('Unit: EventCollector', () => { let collector: EventCollector beforeEach(() => { collector = new EventCollector() }) test('collects events', () => { collector.handleEvent({ type: 'start', messageHistoryLength: 0 }) expect(collector.events).toHaveLength(1) }) })

单元测试无需 API Key、无网络请求,beforeEach中重建被测对象以保证用例隔离。

八、测试数据夹具一览

sdk/e2e/utils/test-fixtures.ts 提供了全套可复用测试数据:

  • SAMPLE_CODE:六段示例代码(简单函数、含 Bug 的除法、含 Bug 的类、异步函数、React 组件等);
  • SAMPLE_DIFFS:单文件与多文件的 git diff 样本(供 commit message、代码审查类测试使用);
  • SAMPLE_PROJECT_FILES:模拟项目文件树(src/index.tssrc/calculator.tspackage.jsonREADME.md),用于projectFiles注入测试;
  • MOCK_WEATHER_DATA/MOCK_DATABASE:天气城市数据与用户表数据,供自定义工具测试使用;
  • TEST_PROMPTS:按场景命名的常用提示词集合;
  • DEFAULT_AGENT/DEFAULT_TIMEOUT:默认 Agent(base2)与默认超时(120 秒)。

九、从测试看 SDK 核心 API

最后从测试反观 SDK 的核心 API 面,帮助理解测试为什么这样写:

  • CodebuffClient(sdk/src/client.ts):构造函数要求apiKey(也可从CODEBUFF_API_KEY环境变量自动读取),核心方法为run(options)checkConnection()
  • RunOptions(sdk/src/run.ts):除agentprompt外,支持previousRun(多轮续聊)、content(多模态文本+图片)、signal(AbortSignal 中止)、params(自定义 Agent 的结构化输入)、extraToolResultsdrainSteeringMessages(运行中注入消息)等高级能力;
  • CodebuffClientOptions(sdk/src/run.ts):projectFilesknowledgeFilesagentDefinitionscustomToolDefinitionsmaxAgentStepshandleEventhandleStreamChunkoverrideTools(替换内置工具实现)、terminalCommandBroker等,均可作为客户端级或单次 run 级配置传入。

测试中常用的agent标识符格式为publisher/agentId@version(如codebuff/base@latestcodebuff/base2@latest),也可直接传AgentDefinition对象,这在前述自定义 Agent 测试中已有充分演示。

十、总结:一套"可本地运行、可 CI 验证、可上手复刻"的测试体系

sdk/e2e测试体系最值得借鉴的设计在于三点:

  1. Mock 与真实调用的无缝切换:通过RUN_CODEBUFF_E2E环境变量决定走 Mock(本地确定性运行)还是真实 API(CI 全链路验证),getApiKey()setupE2eMocks()让测试代码本身无需任何分支;
  2. 统一的断言中枢EventCollector收敛了事件收集、类型过滤、顺序校验、文本拼接等全部断言能力,使测试用例极度精简;
  3. 从机制到行为的全覆盖:集成测试锁死事件类型与顺序契约,E2E 测试验证多轮对话、并发流、子 Agent 流、自定义工具等真实工作流,示例脚本则成为 SDK 使用者的上手教材。

如果你正准备为 Codebuff SDK 编写集成测试,建议直接从 sdk/e2e/utils/event-collector.ts 与 sdk/e2e/utils/get-api-key.ts 这两个工具类入手,再参照 event-ordering.integration.test.ts 与 weather-agent.e2e.test.ts 的模板搭建自己的用例——这套分层清晰、可复用的测试结构,值得每个接入 SDK 的项目直接借鉴。

【免费下载链接】freebuffThe free coding agent项目地址: https://gitcode.com/GitHub_Trending/cod/freebuff

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 19:19:22

如何快速修改AI短剧的镜头和替换包装?

如何快速修改AI短剧的镜头和替换包装&#xff1f;特种猫的做法是正片和包装分层管理&#xff1a;镜头只替换有问题的单个分镜&#xff0c;包装用独立模板轨道一键覆盖&#xff0c;不重跑整集。截至 2026 年&#xff0c;创作者普遍踩3个坑&#xff1a;改1个镜头要整集重新生成、…

作者头像 李华
网站建设 2026/9/15 19:17:47

Unity角色口型同步与眼神模拟:SALSA With RandomEyes实战调优指南

学过几年Unity动画&#xff0c;接手过不少数字人、对话NPC的项目&#xff0c;我敢说在“让角色开口说话”这件事上&#xff0c;最让我省心的方案就是SALSA With RandomEyes。这个插件从名字就能看出来&#xff0c;它干两件事&#xff1a;SALSA负责说话时的口型同步&#xff0c;…

作者头像 李华