1. 为什么你的项目需要 Codex 自动测试
很多人第一次听到「自动测试」四个字,脑子里浮现的是加班到深夜、一行行手写expect的画面。我一开始也这么想,直到被一个真实事故教育了:改了一个价格计算函数的参数顺序,本地跑页面看着没问题,上线后购物车金额全错,客服电话被打爆。那次之后我才明白,测试不是给领导看的 KPI,而是你代码的「安全网」——它在你改动代码的瞬间告诉你哪里塌了。
Codex 自动测试要解决的问题很具体:让 AI 帮你写测试用例,你只负责审查和运行。它适合三类人:刚学前端、还没养成写测试习惯的新手;维护老项目、想补测试但不知道从哪下手的开发者;以及用 Vue3 + Vite 做业务、天天改工具函数却不敢重构的人。核心检索词就三个:Codex、自动测试、Vitest 测试用例。
传统写测试的流程是:读源码 → 想边界条件 → 写 describe/it → 跑 → 报错 → 改。Codex 把这个流程压缩成一句话提示词,它读完你的源文件后直接产出可运行的测试文件。但注意,AI 生成的测试不是免检产品,它可能漏掉空值、可能把断言写反、可能 mock 得不对。所以本文交付的不是「一键生成就完事」,而是完整的生成 → 运行 → 修正闭环,让你跑通第一个自动测试流程后,能自己判断 AI 写的测试靠不靠谱。
下面我会用一个价格工具函数src/utils/price.ts作为被测试对象,从零配置 Vitest 环境开始,到让 Codex 生成单元测试,再到运行失败后让 AI 分析原因并修复。全程命令可复制,配置片段可直接落盘。你跟着做一遍,就能把这套方法迁移到自己项目的任意模块上。
2. TaoToken 前置准备:拿到 Key 并接入 Codex
Codex 本身是命令行里的编码助手,它要调用大模型才能生成测试代码,所以你需要一个稳定的模型接入点。这里用 TaoToken 来做前置配置,它提供 OpenAI 兼容的接口,Codex 可以直接对接。整个准备分三步:注册拿 Key、配置 Codex 的认证文件、验证连通性。
先说拿 Key。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册后在控制台里创建一个 API Key。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,进去后左侧菜单找「API Keys」,点新建,复制那串sk-开头的字符串。这个 Key 只显示一次,丢了就得重建,所以先粘到安全的地方。
接下来配置 Codex。Codex 读取的是~/.codex/auth.json这个文件(Windows 下是C:\Users\你的用户名\.codex\auth.json)。如果目录不存在就手动建一个。文件内容长这样:
{ "OPENAI_API_KEY": "sk-你从TaoToken复制的Key", "OPENAI_BASE_URL": "https://taotoken.net/api" }注意OPENAI_BASE_URL后面不要加/v1,Codex 会自己拼路径。这一点很多人踩坑,加了/v1会变成/v1/v1/chat/completions,直接 404。保存后,在终端里跑一句验证:
codex --version能打印版本号说明 Codex 装好了。再跑一个最小请求确认 Key 有效:
curl https://taotoken.net/api/v1/models \ -H "Authorization: Bearer sk-你的Key"返回一个 JSON 列表,里面有模型 ID,就说明 Key 和网络都通了。如果返回 401,说明 Key 复制错了或者多了空格;如果返回连接超时,检查一下是不是公司网络限制了外部请求。
模型 ID 这块,Codex 默认会用配置里的模型,你也可以在提示词里显式指定。常用的编码模型 ID 可以在接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 里查到。如果你打算长期用 Codex 做编码和 Agent 任务,可以考虑 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它按周期计费,比按量付费更适合高频写测试的场景。
三件套记牢:Base URL 是https://taotoken.net/api,Key 是sk-开头那串,Model ID 从文档里选一个编码能力强的。这三个填对,Codex 才能正常工作。配置完别急着写测试,先在项目根目录跑一次codex进交互模式,随便问一句「你好」,能回话就说明链路通了。
3. 可复制配置:Vitest 环境初始化与 Codex 提示词模板
环境配置是新手最容易卡住的地方。我见过太多人测试文件写好了,跑npm run test报「command not found」,或者报「document is not defined」,本质都是 Vitest 没配对。这一节给你两样东西:一份可直接落盘的 Vitest 配置,一套可复用的 Codex 提示词模板。
先装依赖。假设你是 Vue3 + Vite 项目,在根目录执行:
npm install -D vitest @vue/test-utils jsdom @vitejs/plugin-vue四个包各司其职:vitest是测试运行器,@vue/test-utils提供mount挂载组件,jsdom模拟浏览器 DOM 环境,@vitejs/plugin-vue让 Vitest 能解析.vue单文件组件。装完在package.json的scripts里加一行:
{ "scripts": { "test": "vitest run", "test:watch": "vitest" } }vitest run跑一次就退出,适合 CI;vitest是监听模式,改文件自动重跑,本地开发用这个。
然后是配置文件vitest.config.ts,放在项目根目录:
import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { environment: 'jsdom', globals: true, include: ['src/**/*.{test,spec}.{ts,js}'], coverage: { provider: 'v8', reporter: ['text', 'html'] } } })逐行解释关键项:environment: 'jsdom'让测试里能用document、window,测组件必须开;globals: true让你不用每个文件都import { describe, it, expect },省事;include限定测试文件范围,避免它去扫node_modules;coverage是覆盖率配置,provider: 'v8'比默认的 istanbul 快,reporter里text在终端看,html生成网页报告。
配置完先跑一次空测试确认环境没问题:
npm run test如果提示「No test files found」,说明配置生效了,只是还没写测试。如果报Cannot find module 'jsdom',回去检查依赖装没装。
现在到 Codex 提示词模板。我实测下来,提示词写得越结构化,生成的测试质量越高。模板如下:
帮我给 src/utils/price.ts 写完整的单元测试,使用 Vitest。 要求覆盖以下场景: 1. 正常输入(整数、小数) 2. 边界值(0、最大值、折扣为 0 和 1) 3. 异常输入(负数、超出范围的折扣) 4. 精度问题(小数乘法后的舍入) 测试文件放在 src/utils/__tests__/price.test.ts。 每个 it 的描述用中文,断言要具体,不要用 toBeTruthy 这种模糊断言。这个模板的四个要点:指定文件路径让 Codex 知道读哪个源文件、列出场景避免它只写 happy path、指定输出路径方便你直接落盘、约束断言风格防止它写expect(result).toBeDefined()这种没意义的测试。把这套模板存成代码片段,以后换文件只改路径和场景列表就行。
4. 验证请求:生成-运行-修正完整闭环
配置就绪,现在跑一次完整流程。被测试的源文件src/utils/price.ts内容如下:
export function calculateDiscount(price: number, discount: number): number { if (price < 0) throw new Error('价格不能为负数') if (discount < 0 || discount > 1) throw new Error('折扣必须在0-1之间') return Math.round(price * discount * 100) / 100 } export function formatPrice(price: number): string { return `¥${price.toFixed(2)}` }在项目根目录启动 Codex,把上一节的提示词模板粘进去。它会读文件、分析逻辑、生成测试。生成的src/utils/__tests__/price.test.ts大致如下:
import { describe, it, expect } from 'vitest' import { calculateDiscount, formatPrice } from '../price' describe('calculateDiscount', () => { it('应正确计算折扣价', () => { expect(calculateDiscount(100, 0.8)).toBe(80) expect(calculateDiscount(200, 0.5)).toBe(100) }) it('应正确处理小数精度', () => { expect(calculateDiscount(99.9, 0.7)).toBe(69.93) }) it('价格为0时返回0', () => { expect(calculateDiscount(0, 0.8)).toBe(0) }) it('折扣为1时返回原价', () => { expect(calculateDiscount(100, 1)).toBe(100) }) it('价格为负数时抛出错误', () => { expect(() => calculateDiscount(-1, 0.8)).toThrow('价格不能为负数') }) it('折扣超出范围时抛出错误', () => { expect(() => calculateDiscount(100, 1.5)).toThrow('折扣必须在0-1之间') }) }) describe('formatPrice', () => { it('应正确格式化整数价格', () => { expect(formatPrice(100)).toBe('¥100.00') }) it('应正确格式化小数价格', () => { expect(formatPrice(99.9)).toBe('¥99.90') }) })现在运行:
npm run test预期输出类似:
✓ src/utils/__tests__/price.test.ts (8 tests) 12ms Test Files 1 passed (1) Tests 8 passed (8)8 个测试全绿,说明 AI 生成的测试和源码逻辑一致。但别急着庆祝,真正的价值在失败场景。我故意把源码里的Math.round(price * discount * 100) / 100改成price * discount,再跑一次:
FAIL src/utils/__tests__/price.test.ts > calculateDiscount > 应正确处理小数精度 AssertionError: expected 69.93000000000001 to be 69.93测试精准抓到了浮点精度问题。这时候把报错信息连同源码一起丢给 Codex:
运行 npm run test 后,calculateDiscount 的小数精度测试失败, 报错 expected 69.93000000000001 to be 69.93。 请分析原因并修复源码(优先改源码,不要改测试断言)。Codex 会指出浮点乘法精度丢失,建议恢复Math.round舍入逻辑。改完再跑,全绿。这个「生成 → 运行 → 失败 → 修正」的循环,就是你以后每次改代码都要走的流程。测试不是写完就扔,它是活的,跟着源码一起演进。
5. 常见报错排查:401、local proxy failed 与 reading choices
跑 Codex 自动测试的过程中,报错基本集中在两类:模型接入报错和测试运行报错。我把踩过的坑按报错原文列出来,你对照着查。
401 Unauthorized。这是最常见的接入错误,终端里长这样:
Error: 401 Unauthorized - {"error":{"message":"Invalid API key"}}三个原因:Key 复制时带了空格或换行、Key 已过期被删、auth.json里字段名写错。排查方法:重新从控制台复制 Key,用cat ~/.codex/auth.json检查文件内容,确认OPENAI_API_KEY的值前后没有多余字符。如果 Key 没问题,检查OPENAI_BASE_URL是不是写成了https://taotoken.net/api/(末尾多了斜杠),去掉斜杠再试。
local proxy failed。报错原文:
Error: local proxy failed: connect ECONNREFUSED 127.0.0.1:7890这说明你的终端环境变量里配了本地代理,但代理服务没启动。检查HTTP_PROXY和HTTPS_PROXY两个环境变量:
echo $HTTP_PROXY echo $HTTPS_PROXY如果有值且你不需要代理,直接unset HTTP_PROXY HTTPS_PROXY清掉,再跑 Codex。注意,TaoToken 的接口是直连的,不需要任何代理配置,环境里残留的代理设置反而会干扰。
reading choices 报错。这个通常出现在模型返回格式异常时:
TypeError: Cannot read properties of undefined (reading 'choices')原因是接口返回的 JSON 结构里没有choices字段,多半是 Base URL 拼错了导致请求打到了错误端点。确认OPENAI_BASE_URL是https://taotoken.net/api,不带/v1。如果还报,用 curl 手动打一次接口看返回结构:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{"model":"你的模型ID","messages":[{"role":"user","content":"hi"}]}'返回里有choices数组就说明接口正常,问题在 Codex 配置;返回错误信息就按错误提示处理。
OAuth 相关报错。如果你之前用过其他需要 OAuth 登录的工具,auth.json里可能残留了tokens字段,和OPENAI_API_KEY冲突。最干净的做法是删掉整个auth.json重建,只保留OPENAI_API_KEY和OPENAI_BASE_URL两个字段。
测试侧报错。ReferenceError: document is not defined说明vitest.config.ts里environment没设成jsdom。Cannot find module '@vue/test-utils'说明依赖没装全,重跑npm install -D @vue/test-utils。No test files found检查include路径和实际测试文件位置是否匹配。
排查顺序建议:先 curl 验证 Key 和接口,再检查auth.json字段,最后看测试配置。三件套(Base URL + Key + Model ID)任何一件不对都会报错,逐个确认比瞎改快得多。
6. 把自动测试变成习惯:CTA 与下一步
跑通第一个自动测试后,你要做的不是停下来,而是把它变成肌肉记忆。我的做法是:每次改完一个工具函数,立刻让 Codex 补测试;每次修完一个 bug,让 Codex 针对这个 bug 写一条回归测试。回归测试的意思是,把导致 bug 的输入固定下来,以后任何人改这块代码,测试都会拦住他。这样你的测试集就是项目的历史事故档案,越攒越值钱。
具体操作上,把第 3 节的提示词模板存进你的代码片段库,改文件路径就能复用。测试文件统一放__tests__目录,命名跟源文件对应,比如price.ts对应price.test.ts。跑测试用npm run test:watch,改代码自动重跑,反馈延迟控制在秒级。覆盖率报告用npx vitest run --coverage生成,打开coverage/index.html看哪些行没被覆盖,优先补核心逻辑。
如果你还没配好 Codex 的接入,回到第 2 节,去 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 拿 Key,配置文档在接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 里。想先感受一下模型生成测试的效果,可以去模型对话 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 里贴一段源码试试。长期用 Codex 做编码和 Agent 任务的话,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 比按量付费划算。
最后说个我自己的习惯:每次 Codex 生成测试后,我会花 30 秒扫一遍断言,重点看三处——边界值有没有覆盖 0 和最大值、异常分支有没有toThrow、断言是不是具体到数值。AI 写得快,但判断测试有没有意义是你的活。测试是代码的安全网,网眼大小得你自己定。下一篇讲用 Codex 生成 README 文档,同样是「生成 → 审查 → 修正」的套路,感兴趣可以接着看。