测试指南:从环境搭建到测试用例编写)
Continue VS Code 扩展端到端E2E测试指南从环境搭建到测试用例编写【免费下载链接】continueopen-source coding agent项目地址: https://gitcode.com/GitHub_Trending/co/continue导读本文基于 Continue 开源仓库中 VS Code 扩展的 E2E端到端测试工程实践系统讲解如何搭建 E2E 测试环境、按改动范围选择最快的验证循环、理解 selectors / actions / tests 三层测试架构以及如何通过mock与test两种 LLM Provider 实现完全离线的确定性测试。读完本文你将掌握 Continue 扩展 E2E 测试的完整运行体系与编写规范并能在自己的改动上快速复现这套验证流程。Continue 的 E2E 测试位于 extensions/vscode/e2e 目录所有运行入口统一封装在 extensions/vscode/package.json 的scripts中配套的说明文档即 extensions/vscode/e2e/README.md。一、首次运行完整环境搭建在首次运行 E2E 测试时需要完成构建扩展、编译测试代码、下载 VS Code 与 chromedriver、签名、安装 VSIX 等一系列准备工作。仓库为此封装了一个一键命令npm run e2e:all从 extensions/vscode/package.json 中可以看到e2e:all实际是一条由多个子步骤串起来的流水线npm run e2e:build \ npm run e2e:compile \ npm run e2e:create-storage \ npm run e2e:get-chromedriver \ npm run e2e:get-vscode \ npm run e2e:sign-vscode \ npm run e2e:copy-vsix \ npm run e2e:install-vsix \ npm run e2e:install-extensions \ CONTINUE_GLOBAL_DIRe2e/test-continue npm run e2e:test \ npm run e2e:clean各步骤职责如下脚本作用e2e:build构建 GUI 前端gui目录下npm run build并打包 VSIX 扩展e2e:compile使用 tsconfig.e2e.json 将e2e目录下的 TypeScript 测试代码编译到_outpute2e:create-storage创建./e2e/storage目录用于存放下载的 VS Code 与 chromedrivere2e:get-chromedriver/e2e:get-vscode通过extestvscode-extension-tester 的 CLI下载指定版本--code_version 1.95.0的 VS Code 与对应 chromedrivere2e:sign-vscode对 macOS 上的 VS Code.app 进行 ad-hoc codesign仅 macOS 需要e2e:copy-vsix执行 extensions/vscode/e2e/get-latest-vsix.sh从build目录中取出最新的continue-*.vsix并复制为./e2e/vsix/continue.vsixe2e:install-vsix将 VSIX 安装到./e2e/.test-extensionse2e:install-extensions从 Marketplace 安装ms-vscode-remote.remote-ssh、remote-containers、remote-wsl等远程扩展e2e:test以NODE_ENVe2e运行extest run-tests默认执行./e2e/_output/tests/*.test.js并通过环境变量CONTINUE_GLOBAL_DIRe2e/test-continue指定测试用的全局配置目录e2e:clean清理_output与storage临时产物其中CONTINUE_GLOBAL_DIR是贯穿整个测试体系的关键环境变量它决定了 Continue 扩展在测试进程中读取的全局配置目录。e2e:all使用e2e/test-continueJSON 配置而 YAML 配置的验证则通过e2e:ci:run-yaml脚本以CONTINUE_GLOBAL_DIRe2e/test-continue-yaml运行。非 macOS 平台的差异由于codesign仅在 macOS 上可用非 macOS 系统请改用npm run e2e:all-non-mac对比 extensions/vscode/package.json 可以看到e2e:all-non-mac与e2e:all的唯一区别就是去掉了e2e:sign-vscode步骤其余流程完全一致。二、按改动类型选择最快的验证循环完整跑一遍e2e:all的成本较高需要重新构建 GUI、打包 VSIX、下载依赖。仓库针对「你改了什么代码」提供了三种更快的循环这是 E2E 日常开发中最实用的部分你的改动推荐命令原因只改 E2E 测试代码或config.yaml/config.jsonnpm run e2e:quick扩展与 GUI 无需重新打包改了扩展extension代码npm run e2e:recompile需要重新package生成 VSIX 并重新安装改了 GUI 代码npm run e2e:rebuild-gui需要重新构建 GUI 产物并打包进 VSIX三者对应的脚本实现见 extensions/vscode/package.json# e2e:quick —— 仅重编译测试代码复用已安装的扩展 npm run e2e:compile CONTINUE_GLOBAL_DIRe2e/test-continue npm run e2e:test npm run e2e:clean # e2e:recompile-extension —— 重新打包扩展并重装 npm run package npm run e2e:compile npm run e2e:copy-vsix npm run e2e:install-vsix npm run e2e:install-extensions CONTINUE_GLOBAL_DIRe2e/test-continue npm run e2e:test npm run e2e:clean # e2e:rebuild-gui —— 重新构建 GUI 并打包进扩展 rm -rf gui cp -r ../../gui/dist gui npm run package npm run e2e:copy-vsix npm run e2e:install-vsix npm run e2e:install-extensions CONTINUE_GLOBAL_DIRe2e/test-continue npm run e2e:test npm run e2e:clean选择原则很简单改动面越小循环越快。如果只是修了一个测试用例的 selector跑e2e:quick就够了如果改了扩展的主逻辑或 GUI 界面才需要走重打包流程。三、测试代码的三层架构所有 E2E 测试按目录职责被严格拆分为三层见 extensions/vscode/e2e 目录结构目录职责现有文件selectors返回页面元素的函数封装元素定位细节selectors/Apply.selectors.ts、selectors/Autocomplete.selectors.ts、selectors/Edit.selectors.ts、selectors/GUI.selectors.ts、selectors/NextEdit.selectors.ts、selectors/SSH.selectors.ts、selectors/SelectorUtils.tsactions在编辑器上执行操作的动作函数输入、点击、切换模式等actions/Apply.actions.ts、actions/Autocomplete.actions.ts、actions/Edit.actions.ts、actions/GUI.actions.ts、actions/Global.actions.ts、actions/KeyboardShortcuts.actions.ts、actions/NextEdit.actions.tstests真正的测试用例通常是较长的功能链路而非单个动作tests/Autocomplete.test.ts、tests/Edit.test.ts、tests/GUI.test.ts、tests/KeyboardShortcuts.test.ts、tests/PromptFile.test.ts 等这种分层设计保证了selector 只关心「元素在哪里」action 只关心「怎么操作」test 只关心「验证什么」。当 UI 结构调整时通常只需修改 selector 层测试用例本身保持稳定。以 GUI 元素定位为例selectors/GUI.selectors.ts 中的定位函数大量依赖data-testid属性例如submit-input-button、accept-tool-call-button、model-select-button、tool-policy-item-${toolName}等这些定位符与 GUI 前端gui 目录下的 React 组件中设置的data-testid一一对应。再看 actions/Global.actions.ts 中的动作封装示例openTestWorkspace()通过VSBrowser.instance.openResources(e2e/test-continue)打开默认测试工作区并清理所有通知createAndOpenNewTextFile()走「新建文件 → 选择 Text File → 打开 Untitled-1」的完整交互链路setNextEditEnabled()会先设置环境变量CONTINUE_E2E_NON_NEXT_EDIT_TEST再通过状态栏查找包含 Continue 的文本并判断是否需要执行Continue: Toggle Next Edit命令来开关 Next Edit 功能。测试用例层则把上述动作串成完整的功能路径例如 tests/Autocomplete.test.ts 中的before钩子会先GlobalActions.disableNextEdit()关闭 Next Edit 以避免干扰beforeEach打开测试工作区并新建文件随后用AutocompleteActions.testCompletions验证自动补全展示、用AutocompleteActions.forceCompletion验证「Continue: Force Autocomplete」命令能强制触发补全对应扩展在 extensions/vscode/package.json 中注册的ctrlaltspace/cmdaltspace快捷键。四、测试基建超时约定与常用工具超时层级extensions/vscode/e2e/constants.ts 定义了统一的超时基线BASELINE 15_00015 秒并派生出一组语义化超时const BASELINE 15_000; export const DEFAULT_TIMEOUT { XS: BASELINE * 0.2, // 3 秒 SM: BASELINE * 0.5, // 7.5 秒 MD: BASELINE, // 15 秒 XL: BASELINE * 5, // 75 秒 XXL: BASELINE * 7, // 105 秒 XXLP: BASELINE * 8, // 120 秒 };测试中的this.timeout(...)、waitForSuccess(...)均使用这套语义化超时普通元素等待用MD首次加载或较慢操作如第一次自动补全需要等待 Continue 初始化用XL超长链路如发送 20 条消息用XL * 1000级别。TestUtils 工具集extensions/vscode/e2e/TestUtils.ts 提供了测试中复用的核心工具waitForSuccess(locatorFn, timeout, interval)轮询执行定位函数直到成功默认超时DEFAULT_TIMEOUT.MD、默认间隔 500ms。其注释特别提醒很多场景下也可以直接使用 Selenium 原生的driver.wait(until.elementLocated(...))或waitForAttributeValuewaitForTimeout(ms)等待固定时长用于处理两个事件之间的时序问题expectNoElement(locatorFn)在超时窗口内持续断言某元素始终不出现默认 1 秒、200ms 间隔用于验证元素被删除或消失generateTestMessagePair(id)生成成对的测试消息TEST_USER_MESSAGE_id与TEST_LLM_RESPONSE_id与 TestLLM 的响应逻辑严格对应isMacOS与osControlKey平台判断与操作系统控制键macOS 返回Key.META其余返回Key.CONTROL用于跨平台快捷键模拟getGlobalContextFilePath()拼接出CONTINUE_GLOBAL_DIR/index/globalContext.json的路径。五、测试如何连接 LLMmock 与 test 双 Provider 机制E2E 测试不能依赖真实的外部模型 API。README 明确指出这取决于test-continue/config.json和test-continue/config.yaml中写入的模型 Provider——Provider 为mock时使用MockLLM为test时使用TestLLM。两套测试配置仓库同时维护 JSON 与 YAML 两套测试配置分别对应CONTINUE_GLOBAL_DIR指向e2e/test-continueextensions/vscode/e2e/test-continue/config.json与e2e/test-continue-yamlextensions/vscode/e2e/test-continue-yaml/config.yaml。这样既验证了扩展功能也顺带验证了两种配置格式的解析YAML 配置解析逻辑见 core/config/yaml。JSON 版本的核心模型配置如下YAML 版本语义一致{ models: [ { title: TEST LLM, provider: test, model: this field is not used }, { title: Mock, provider: mock, model: this field is not used }, { provider: mock, title: TOOL MOCK LLM, model: claude-sonnet-4-5, capabilities: { tools: true }, requestOptions: { extraBodyProperties: { chatStream: [ [ { role: assistant, content: Im going to call a tool: }, { role: assistant, content: , toolCalls: [ { id: test_id, type: function, function: { name: view_diff } } ] } ], [REPEAT_LAST_MSG] ] } } } ], systemMessage: TEST_SYS_MSG, tabAutocompleteModel: { title: TEST LLM, provider: test, model: this field is not used }, tabAutocompleteOptions: { useCache: false }, contextProviders: [ { name: docs }, { name: diff }, { name: url }, { name: folder }, { name: terminal } ] }其中TOOL MOCK LLM、SYSTEM MESSAGE MOCK LLM、LAST MESSAGE MOCK LLM三个「剧本型」Mock 模型通过requestOptions.extraBodyProperties.chatStream注入预设的对话流REPEAT_SYSTEM_MSG表示把系统消息原样回显REPEAT_LAST_MSG表示回显最后一条用户消息用于分别验证 Agent 工具调用、系统消息展示、消息回显等场景。底层实现MockLLM实现在 core/llm/llms/Mock.ts其providerName为mock构造函数从options.requestOptions?.extraBodyProperties?.chatStream读取预设的MockMessage[][]并按序回放从而在完全离线的条件下模拟任意 LLM 行为包括带toolCalls的工具调用流。TestLLM实现在 core/llm/llms/Test.ts其providerName为test内部用正则/[^0-9]*TEST_USER_MESSAGE_(\d)[^0-9]*/g从输入中提取消息编号并回显为对应的TEST_LLM_RESPONSE_编号。这与TestUtils.generateTestMessagePair()生成的消息对严格对应也是 tests/Autocomplete.test.ts 中断言补全内容等于TEST_LLM_RESPONSE_0/TEST_LLM_RESPONSE_1的依据。六、测试失败排查清单README 给出了四条最常见的失败原因结合源码可以整理为可操作的排查顺序data-testid位置错误是否把data-testid放在了 React 组件上而不是真正的 HTML 元素上GUI selector 的定位如 selectors/GUI.selectors.ts 中的SelectorUtils.getElementByDataTestId只能命中真实 DOM 元素组件容器不会渲染成可点击的 HTML 节点。本地与 CI 配置不一致本地是否有config.yaml但 CI 中运行的是config.json注意e2e:ci:run使用CONTINUE_GLOBAL_DIRe2e/test-continueJSON而e2e:ci:run-yaml使用e2e/test-continue-yamlYAML。若测试依赖某套配置的行为务必确认 CI 实际加载的是哪一套。Selector 本身写错认真核对 XPath / CSS 与页面上真实的属性值。可先借助 selectors/SelectorUtils.ts 封装的工具确认元素定位方式。竞态条件race condition如果测试行为不一致时好时坏可以在两个事件之间插入TestUtils.waitForTimeout(...)缓解时序问题但 README 特别提醒这类修复可能在将来引入 flake不稳定因此应尽量定位真正的异步边界。更稳妥的方式是使用TestUtils.waitForSuccess(...)做条件轮询直到元素满足预期状态再继续而不是盲目 sleep。此外从 tests/GUI.test.ts 可以看到一些值得借鉴的断言手法聊天发送验证输入TEST_USER_MESSAGE_0后按回车或点发送按钮再用waitForSuccess(() getThreadMessageByText(view, llmResponse))等待并断言TEST_LLM_RESPONSE_0出现删除消息验证删除后配合TestUtils.expectNoElement断言旧消息确实消失工具调用验证切换TOOL MOCK LLM与 Agent 模式后断言状态栏出现 Continue viewed the git diff对应view_diff工具并可配合toggleToolPolicy模拟「需要审批」的授权流程验证接受/拒绝工具调用两条路径系统消息验证切换到SYSTEM MESSAGE MOCK LLM后断言聊天中出现TEST_SYS_MSG。七、平台差异与其他注意事项macOS 专属步骤codesign只存在于 macOS因此非 macOS 平台必须使用npm run e2e:all-non-mac跳过签名步骤CI 复用仓库为 CI 拆分出了e2e:ci:download仅下载 VS Code 与 chromedriver可缓存与e2e:ci:run/e2e:ci:run-yaml运行编译与测试便于在 CI 中先缓存依赖再执行Next Edit 干扰自动补全类测试如 tests/Autocomplete.test.ts在before钩子中通过GlobalActions.disableNextEdit()关闭 Next Edit避免其与自动补全互相干扰——编写新的测试时若涉及编辑器内联行为也应注意隔离这类功能开关工作区路径约定默认测试工作区为e2e/test-continue目录见 actions/Global.actions.ts 中defaultFolder e2e/test-continue新建文件、保存文件等动作都围绕该工作区展开。八、进一步阅读若希望深入 E2E 测试与 Continue 内部实现可在当前仓库中继续查阅测试入口与全部脚本extensions/vscode/package.jsonE2E 测试目录结构与各类用例extensions/vscode/e2e含 tests、selectors、actionsMock 与 Test LLM 实现core/llm/llms/Mock.ts、core/llm/llms/Test.ts测试用全局配置extensions/vscode/e2e/test-continue/config.json、extensions/vscode/e2e/test-continue-yaml/config.yaml【免费下载链接】continueopen-source coding agent项目地址: https://gitcode.com/GitHub_Trending/co/continue创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考