1. 目的:mocha 等真实实例,等得人没脾气
vscode 扩展测试等得久?TaoToken 这样配 Codex 查 mocha。先去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 拿一个 Key,再让 Codex 的 base_url 指向 https://taotoken.net/api,然后把 vite.config.ts、.vscode-test.mjs、launch.json 一起交给它,按 mocha 的加载顺序查清等待发生在哪一步。
vscode 插件社区成熟,但总有插件因为维护不及时、功能差一口气而让你想自己动手。动手改插件绕不开测试,而测试最磨人的不是写用例,是等。官方 vscode-test 默认用 mocha,mocha 又要求把 TS 编译成 JS 才能跑;单元测试还能用 vitest 直接跑 TS 源码,集成测试却必须拉起一个真实的 vscode 实例。真实实例启动要几秒,打开工作区、触发命令、断言视图内容,每一步都在等。用例一多,等待不是线性叠加,而是成倍放大。
比“等得久”更难受的是“等不到”。跑集成测试时经常出现这样的画面:终端停在某个 mocha 用例上,既不报错也不继续走,像死锁一样卡在那里。靠肉眼读配置很难定位这种卡顿,因为配置链太长:vite.config.ts 管别名和单元测试范围,.vscode-test.mjs 管 mocha 的加载方式,launch.json 管调试入口,package.json scripts 管编译顺序。任何一个环节多等一拍,整体表现就是测试等得久。
与其“再等等试试”,不如让 AI 编程工具把配置链完整读一遍,按执行顺序指出每拍浪费在哪。Codex 擅长读项目文件、对照配置、解释调用链,但 Codex 自己连的是官方模型通道,额度和模型切换都不够灵活。走 TaoToken 的兼容通道,把 Codex 的 Base URL 换成 https://taotoken.net/api 之后,它就能安心当你的配置审查员。
2. vitest 单元测试:用 .spec.ts 跑 TS 源码,别等 tsc
2.1 单元测试为什么先选 vitest
mocha 不是不能用,而是作为 vscode-test 的默认框架,它要求测试代码先编译成 JS。改完测试源码要等一轮 tsc 编译,再等 mocha 加载编译产物,来回几次就烦了。vitest 直接跑 TS 源码,靠 esbuild 转译,几乎没有编译等待;它还能复用 vite.config.ts 里的 alias 配置,测试代码里写@/utils/parseFile这种路径不用再配一遍。
单元测试统一用 .spec.ts 作为后缀,把目录固定在 src/test/unit 下,和集成测试的 .test.ts 后缀区分开。这样 vitest 的 include 只圈到单元测试,集成测试目录里的文件不会因为被单元测试扫到而报错。
2.2 vite.config.ts:include 和 alias 是核心
vite.config.ts 长这样:
/// <reference types="vitest" /> import path from 'path'; import { defineConfig } from 'vite'; export default defineConfig({ resolve: { alias: [ { find: /^~(.+)/, replacement: path.join(process.cwd(), 'node_modules/$1') }, { find: /^@\/(.+)/, replacement: path.join(process.cwd(), 'src/$1') }, ], }, test: { include: ['src/test/unit/**/*.spec.ts'], coverage: { exclude: ['node_modules', 'out', 'src/test', 'src/typings', '.vscode-test'], }, }, });第一处要核对的是 test.include。它只匹配 src/test/unit/**/*.spec.ts,如果测试文件放错目录或者后缀写错了,vitest 会一声不响地忽略,你看到的“全绿”其实是“没跑”。第二处是~前缀的 alias,它把import '~/xxx'改写为 node_modules 下的资源路径;扩展里如果引用了某些依赖包内部的静态资源,删掉这一行,构建阶段就会报 module not found。
2.3 package.json 和 tsconfig 的配合
package.json 里补两个脚本:
{ "scripts": { "test:unit": "vitest unit --watch=false", "test:coverage": "vitest run --coverage" } }test:unit 关掉了 watch,适合 CI;本地开发建议直接跑 vitest unit,改一行代码自动重跑,比手动按命令快得多。test:coverage 生成覆盖率报告,coverage.exclude 里要排除 out 和 .vscode-test,不然编译产物和测试宿主目录会混进统计,数字虚高到没有参考价值。
tsconfig.json 里的别名要和 vite 保持一致:
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"] } } }vite 负责运行时解析,tsconfig 负责让 IDE 和 tsc 认识同样的路径。两处不一致时,编辑器不报错,tsc 编译却报 cannot find module,这种问题交给 Codex 对照检查一眼就能抓到。
3. vscode-test 集成测试:ts-node/register 和 launch.json 是重点
3.1 .vscode-test.mjs 里藏着半秒等待
集成测试跑的是真实 vscode 实例,这是官方对 extension 测试的硬性要求,绕不开。但慢不慢,很大程度取决于 mocha 的加载方式。.vscode-test.mjs 的典型配置:
import { defineConfig } from '@vscode/test-cli'; export default defineConfig({ files: 'out/test/**/*.test.js', mocha: { ui: 'bdd', require: ['ts-node/register', 'tsconfig-paths/register'], }, });require 数组是关键。ts-node/register 让 mocha 在运行时现场编译 TS 文件,tsconfig-paths/register 负责在运行时解析 tsconfig 里的 paths 别名。也就是说,每加载一个测试文件,都要现场跑一次 TypeScript 转译,几十个用例叠加,等待感非常明显。
如果你的测试文件已经通过 tsc 编译进了 out/ 目录,mocha 本来可以直接加载编译后的 JS,这两个 register 其实可以去掉。保留它们,换来的是调试时不用重新编译;代价是每次运行都要现编译。排查“等得久”时,先问自己:你等的是真实实例启动,还是 ts-node 在编译?
3.2 launch.json:testConfiguration 和 args 的冲突
断点调试集成测试时,launch.json 要同时管扩展宿主启动和 mocha 的 testConfiguration。一个容易忽略的配置点是:.vscode-test.mjs 里如果有 launchArgs,launch.json 的 args 又会传一份启动参数,两者以 args 为准。为了避免互相覆盖,建议单独建一个 .vscode-test-debug.mjs 给调试用,里面只保留 files 和 mocha,不写 launchArgs。
{ "name": "Test: e2e", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "testConfiguration": "${workspaceFolder}/.vscode-test-debug.mjs", "args": [ "${workspaceFolder}/sampleWorkspace/test.code-workspace", "--extensionDevelopmentPath=${workspaceFolder}", "--disable-extensions" ], "env": { "mode": "debug", "TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json" }, "preLaunchTask": "npm: test-compile", "sourceMaps": true }preLaunchTask 会先执行 npm: test-compile,也就是 tsc 编译加 esbuild 打包。如果每次调试都全量编译,等待时间有一部分其实花在构建上,和 mocha 无关。sourceMaps 必须开着,否则断点映射不回 TS 源码。TS_NODE_PROJECT 指定了 tsconfig 路径,方便调试时 ts-node 找到别名配置。
3.3 package.json 的测试脚本链
{ "scripts": { "clean": "rimraf out/", "test-compile": "npm run clean && tsc -p ./ && npm run compile", "test": "npm run test-compile && vscode-test" } }test 脚本先在本地做三件事:清掉 out/、用 tsc 编译测试文件、用 esbuild 打包扩展本体。npm run compile 不能省,不然测试加载的是上次打包的旧扩展代码;tsc -p ./ 不能省,不然 mocha 只能退回去用 ts-node 现场编译,又回到慢的问题。这里每一步都有明确的产物,适合让 Codex 逐条核对脚本顺序。
4. 自定义 Runner 集成测试:绕开默认的 mocha 触发器
4.1 runTests.js 把控制权拿回来
vscode-test 除了 .vscode-test.mjs 这种声明式配置,还支持自定义 Runner:自己写一个运行入口,调用 @vscode/test-electron 的 runTests API,明确指定扩展路径、测试入口路径、工作区路径。这样做的好处是 CI 里可控性更强,退出码、超时、日志输出都由你说了算。
自定义 Runner 的入口是 out/test/runTests.js,它需要指定两个关键路径:extensionDevelopmentPath 指向扩展源码目录,extensionTestsPath 指向 out/test/suite/index.js。index.js 负责把所有 .test.js 收集起来交给 mocha 执行,它的写法在官方 helloworld-test-sample 里有完整例子,照着改成自己的项目结构即可。
4.2 launch.json 的断点配置
{ "name": "Test: e2e use mocha", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "args": [ "${workspaceFolder}/sampleWorkspace/test.code-workspace", "--disable-extensions", "--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" ], "env": { "NODE_ENV": "test", "TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json" }, "outFiles": ["${workspaceFolder}/out/test/**/*.js"], "preLaunchTask": "npm: test-compile", "sourceMaps": true }和前一种调试配置的区别:这里没有 testConfiguration,改成用 --extensionTestsPath 直接指到测试入口。优点是指令链路短,断点命中后能直接看到 mocha 从哪个入口开始加载用例;缺点是需要自己保证 out/test/suite/index 已经编译出来,编译顺序错了会直接打不开测试入口。
4.3 scripts 设置
{ "scripts": { "test:suite:mocha": "npm run test-compile && node out/test/runTests.js" } }test:suite:mocha 把 clean、tsc 编译、esbuild 打包、node 执行 runner 串成一条命令。注意最后一步是 node 直接跑编译后的 JS,而不是依赖 mocha 的 ts-node 现场编译。两种跑法各有取舍:一个把等待放在编译阶段,一个把等待放在运行阶段。集成测试本身就要等真实实例,编译与运行哪个更划算,可以让 Codex 按你的用例规模算一笔账。
5. 走 TaoToken 的 Codex:让配置链自己交代等待来源
5.1 Codex 的 Base URL 指向 TaoToken
前面的配置都要落地,但真正让“等得久”变成可定位的问题,还需要一个能读完整配置链的帮手。Codex 默认连的是官方模型通道,想换模型、控额度都不太方便。把它接到 TaoToken 的兼容通道,配置写在 ~/.codex/config.toml:
model_provider = "taotoken" [model_providers.taotoken] name = "taotoken" base_url = "https://taotoken.net/api" api_key = "YOUR_API_KEY"YOUR_API_KEY 需要先注册再创建:打开 TaoToken,在控制台创建 API Key,复制到 api_key 字段。模型 ID 不要照抄老教程里的模型名,以 TaoToken 模型广场当天公布的 ID 为准;查到的 ID 可以写进 config.toml 顶层的 model 字段,也可以运行时用 -m 参数指定。注册、创建 Key、查模型、看用量都回到这个落地页完成;而 codex 的 base_url 填 https://taotoken.net/api,末尾没有 /v1。
5.2 一次有针对性的 Codex 对话
Codex 接好后,在项目根目录执行 codex,把下面的问题粘给它:
“项目里有 vite.config.ts、.vscode-test.mjs、launch.json 和 package.json。集成测试走 vscode-test + mocha,最近跑得很慢,有时卡在一个用例上等不到结果。请按 mocha 的实际加载顺序检查这几处:ts-node/register 和 tsconfig-paths/register 是否导致每个测试文件现场编译;launch.json 里 testConfiguration 与 args 是否冲突;preLaunchTask 是否每次全量编译;sampleWorkspace/test.code-workspace 是否让扩展启动时多等了额外时间。只读文件、给结论,不要执行测试。”
最后一句很重要:Codex 只做配置解读和问题定位,真正跑测试还是在你的本地终端执行 npm run test-compile && vscode-test,把输出贴回对话继续追问。这样既不会让 AI 工具直接拉起扩展宿主环境,又能借它的读文件和推理能力,把“慢”拆解成具体的等待环节。
5.3 Codex 给出的典型结论
基于上面的配置,Codex 大概率会指向两个方向。第一个是 ts-node/register 在调试路径里拖慢了加载:测试文件能预编译到 out/ 就预编译,调试时直接跑 .test.js。第二个是 launch.json 同时存在 testConfiguration 和 args,启动参数重复传递,扩展宿主可能加载了两遍插件,等待时间成倍增加。
如果卡在某个用例上一直等不到结果,Codex 还会建议在 before 钩子里加一个固定延时,比如 sleep(500),让真实实例的 UI 操作有足够时间完成。听起来像用等待解决等待,但比在每条断言里猜超时参数要稳定得多。vscode 官方教程也认可这种处理,真实实例的渲染和事件回调本来就不是同步的。
6. 小结:等得久的问题,最终落在配置链的哪一环
6.1 先验证 Codex 通道是否通
配置完 config.toml,先在任意目录执行 codex,问一句“用一句话说明 mocha 的 require 数组的作用”。能正常回答,说明 Key、模型 ID、Base URL 都通了。报 401 的话,八成是 YOUR_API_KEY 没替换成真实 Key,回 TaoToken 控制台检查刚才创建的 Key 是不是复制完整了。
6.2 每一步改动都记录时间
按 Codex 给出的结论逐步改,每改一处就跑一次 npm run test-compile && vscode-test,记录从回车到第一个用例输出的时间。先去掉 .vscode-test.mjs 里多余的 ts-node/register,再看 launch.json 的 testConfiguration 和 args 是否并存,最后在慢用例的 before 里补 sleep。三次改动对应三个不同的等待来源,时间差会告诉你到底是谁拖慢了整体。
6.3 去用量页核对这次排查的成本
排查完成后,回 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 的用量页看一眼刚才和 Codex 的对话消耗。如果多轮反复贴文件,token 会明显偏高;下次直接把 vite.config.ts、.vscode-test.mjs、launch.json 三个文件内容合并粘贴,一次读完再给结论,比挤牙膏式问答更省。用量页也能顺手核对模型 ID 是否选对,避免用错了模型还在傻等。
回到最初的问题:vscode 扩展测试等得久,不是 mocha 一个框架的锅。真实实例启动、ts-node 现场编译、launch.json 参数冲突、preLaunchTask 全量构建,每一环都可能吃掉几秒。官方也在推动测试框架演进,但眼下集成测试绕不开 mocha,能优化的是怎么把等待时间花得明白。走 TaoToken 的 Codex 能把这些环节逐个拆开,把“感觉慢”变成“慢在哪一步”。下次再被集成测试卡住,不用盯着终端发呆,把配置丢给 Codex 做一次体检,让等待变成有结论的排查。