1. 需求文档到 Playwright 用例,卡点到底在哪
Codex 测试实战这件事,很多人第一反应是「让 AI 直接写脚本」。我试过,直接丢一句「帮我给优惠券领取功能写 Playwright 用例」,出来的东西看着像模像样,跑起来全是坑:定位器用 XPath、账号密码硬编码在代码里、断言只校验页面文案、接口路径靠猜。最后花在改脚本上的时间,比自己从零写还多。
问题不在 Codex 本身,而在于输入太模糊。Codex 这类模型的能力边界很清楚:你给它明确的上下文、边界条件、校验标准,它能产出接近可用的初稿;你给它一句模糊需求,它只能还你一份「通用模板」。测试开发真正要做的,是把需求文档拆成模型能消化的结构化输入,再把模型输出收敛到项目规范里。
这条链路我拆成五段:需求验收点拆解、代码影响面扫描、测试点表格化、Playwright 脚本生成、CI 跑通与报错归因。每一段都有可复制的提示词模板和配置片段。模型调用统一走 TaoToken 的 Key,一个 Key 管住 Codex 和后续可能接入的其他模型,省得在多个平台之间来回切。
适合谁看:手上有需求文档、想把它转成可执行自动化用例的测试开发;已经在用 Playwright 但脚本维护成本高的团队;想把 AI 接进测试流程又怕产出不可控的人。下面按步骤走,每步都给命令和配置,跟着做能拿到一份能跑的用例文件。
2. TaoToken 统一 Key 前置:把模型调用收敛到一个入口
在写第一行提示词之前,先把模型调用的入口固定下来。测试流程里会反复调用模型:拆需求、扫代码、生成脚本、分析报错,如果每次都要换平台、换 Key、换计费方式,流程根本跑不顺。TaoToken 的作用就是把这些调用收敛到一个 Key 上。
先拿 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 ,Key 列表在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。创建时给 Key 起个能认出来的名字,比如codex-test-flow,方便后面在 CI 里区分环境。
拿到 Key 之后,接口地址统一用 https://taotoken.net/api ,注意这个地址不带 UTM 参数,是纯 API 端点。模型 ID 按你实际要用的填,Codex 场景下常用的编码模型 ID 可以在模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 里先试跑确认,确认能正常返回再写进配置。
这里有个容易踩的坑:很多人把 Key 直接写进项目里的.env然后提交到仓库。测试项目尤其危险,因为 CI 日志、构建产物都可能带出 Key。正确做法是本地用.env.local(加进.gitignore),CI 里用平台的环境变量注入。下面给一份本地配置片段,路径放在项目根目录的.env.local:
# .env.local —— 本地开发用,务必加入 .gitignore TAOTOKEN_API_KEY=sk-你的Key TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_MODEL_ID=你的模型ID # Playwright 测试环境变量 TEST_BASE_URL=http://localhost:3000 TEST_USERNAME=test_user TEST_PASSWORD=test_passCI 侧(以 GitHub Actions 为例)在仓库 Settings → Secrets 里加TAOTOKEN_API_KEY,工作流里通过env注入,不要写死在 yaml 里。这样本地和 CI 用的是同一套变量名,脚本不用改。
如果你后续要长期跑编码类任务、Agent 类任务,可以看下 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,把额度规划清楚,避免跑到一半 Key 限额了流程断掉。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,参数细节以文档为准。
Key 管好之后,模型调用就可以在脚本里统一封装成一个函数,后面所有提示词都走这个入口。这一步不做,后面每换一个模型就要改一遍代码,测试流程根本没法沉淀。
3. 可复制配置:Codex 提示词模板 + Playwright 配置片段
这一段是整篇的核心,给的是能直接复制粘贴的东西。分三块:需求拆解提示词、脚本生成提示词、Playwright 配置文件。
3.1 需求拆解提示词模板
第一步不是写用例,是让 Codex 站在测试视角读需求。提示词里明确禁止它直接写用例,逼它先输出规则、风险点、分类。模板如下:
你先不要写测试用例。 请站在资深测试工程师视角,阅读下面需求,帮我做三件事: 1. 列出这个功能的核心业务规则 2. 找出最容易出问题的风险点,每个风险点说明为什么需要测 3. 按主流程、异常流程、边界场景、数据一致性、安全风险分类 要求: - 不要输出空泛内容 - 如果需求描述不完整,列出需要追问产品的问题 需求如下: 【粘贴具体业务需求】以优惠券领取为例,模型会输出「单用户限领一次」「活动时间校验」「库存扣减」「重复领取限制」「接口幂等」这些规则。你要做的是人工复核:哪些是需求里写死的,哪些是模型补的常识。模型补的部分要回产品确认,不能直接当验收标准。
3.2 代码影响面扫描提示词
需求拆完,让 Codex 读项目代码,定位功能链路。提示词里强调「不要改代码」,只做定位:
请你先阅读当前项目中和"优惠券领取"相关的代码,不要改代码。 重点帮我找: 1. 前端页面入口 2. 领取按钮的交互逻辑 3. 调用的接口地址 4. 接口返回码和错误提示处理 5. 是否存在防重复提交逻辑 6. 是否有现成的测试用例或自动化脚本 最后输出:涉及文件列表、主要调用链路、测试时最需要关注的风险点。模型输出的文件路径和接口链路必须人工复核。这一步的价值是省掉人工全局搜索的时间,不是替代你理解代码。
3.3 Playwright 脚本生成提示词
这是最容易出垃圾的一步。约束必须写死:框架、定位器优先级、环境变量来源、禁止硬编码。模板:
请根据下面测试点生成 Playwright 自动化脚本。 项目约束: 1. 使用 @playwright/test 2. 优先使用 getByRole、getByText、getByTestId 3. 不要使用 XPath 4. 测试环境地址从 TEST_BASE_URL 读取 5. 登录账号从 TEST_USERNAME、TEST_PASSWORD 读取 6. 不要写死真实域名、真实账号、真实 token 7. 领取成功后,调用接口查询用户券包做二次断言 8. 代码拆成:登录、进入活动页、领取优惠券、查询券包几个方法 测试点: 【粘贴测试点表格】3.4 Playwright 配置文件片段
项目根目录的playwright.config.ts,重点是baseURL从环境变量读、CI 下开启重试和单 worker:
import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', timeout: 30_000, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: process.env.CI ? [['html'], ['github']] : [['list']], use: { baseURL: process.env.TEST_BASE_URL, trace: 'on-first-retry', screenshot: 'only-on-failure', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, ], });baseURL从TEST_BASE_URL读,脚本里所有page.goto('/activity/coupon')都是相对路径,本地和 CI 换环境只改变量,不改代码。trace: 'on-first-retry'在失败重试时留痕,后面报错归因靠它。
3.5 生成后的脚本长什么样
模型按上面约束产出的初稿大致是这样,注意定位器全是getByRole,账号从环境变量读,领取后走接口二次断言:
import { test, expect } from '@playwright/test'; const baseUrl = process.env.TEST_BASE_URL; const username = process.env.TEST_USERNAME; const password = process.env.TEST_PASSWORD; async function login(page) { await page.goto(`${baseUrl}/login`); await page.getByRole('textbox', { name: '账号' }).fill(username); await page.getByRole('textbox', { name: '密码' }).fill(password); await page.getByRole('button', { name: '登录' }).click(); await expect(page.getByText('首页')).toBeVisible(); } async function openActivityPage(page) { await page.goto(`${baseUrl}/activity/coupon`); await expect(page.getByText('优惠券领取')).toBeVisible(); } async function receiveCoupon(page) { await page.getByRole('button', { name: '立即领取' }).click(); } test('用户成功领取优惠券后,券包中可以查询到该券', async ({ page, request }) => { await login(page); await openActivityPage(page); await receiveCoupon(page); await expect(page.getByText('领取成功')).toBeVisible(); const response = await request.get(`${baseUrl}/api/user/coupons`); expect(response.status()).toBe(200); const data = await response.json(); expect(data.list.some(item => item.couponName === '活动优惠券')).toBeTruthy(); });生成后必做一步 Review,提示词:
请你 review 上面的 Playwright 脚本,重点检查: 1. 是否存在不稳定定位 2. 是否依赖脏数据 3. 是否缺少等待或断言 4. 是否有账号、域名、token 等敏感信息 5. 是否适合接入 CI 执行 直接指出问题并给修改建议。这一步能拦掉大部分「看着能跑、实际不稳」的脚本。
4. 验证请求:本地跑通到 CI 通过记录
配置和脚本都有了,接下来是验证。分本地和 CI 两段。
本地先装依赖:
npm init -y npm i -D @playwright/test npx playwright install chromium把上面生成的用例存成tests/coupon.spec.ts,.env.local填好变量。Playwright 默认不读.env,需要装dotenv或在命令里注入。简单做法:
export $(grep -v '^#' .env.local | xargs) npx playwright test tests/coupon.spec.ts --project=chromium跑通后终端会输出类似:
Running 1 test using 1 worker 1 passed (3.2s)如果失败,先看test-results/下的截图和 trace,再决定是脚本问题还是环境问题。本地通过后,接 CI。GitHub Actions 工作流片段:
name: playwright-tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx playwright install --with-deps chromium - run: npx playwright test env: TEST_BASE_URL: ${{ secrets.TEST_BASE_URL }} TEST_USERNAME: ${{ secrets.TEST_USERNAME }} TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }} TAOTOKEN_API_KEY: ${{ secrets.TAOTOKEN_API_KEY }} - uses: actions/upload-artifact@v4 if: failure() with: name: playwright-report path: playwright-report/CI 里workers: 1是为了避免并发抢库存导致用例互相干扰,等测试数据隔离做好再放开。跑通后 Actions 页面会显示绿色通过记录,失败时 artifact 里有完整报告。
验证模型调用是否正常,可以在本地单独发一次请求确认 Key 有效:
curl -s https://taotoken.net/api/v1/models \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" | head -c 300返回模型列表说明 Key 和端点都通。这一步在 CI 里也可以加个前置检查,Key 失效时快速失败,不用等用例跑完才发现。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth
这一段按真实报错来,每个都给现象、原因、处理。
401 Unauthorized。现象是请求模型接口返回 401,或 CI 里提示鉴权失败。原因通常是 Key 没注入、Key 拼写错、或者环境变量名对不上。排查顺序:先确认TAOTOKEN_API_KEY在当前 shell 里echo得出来;再确认请求头是Authorization: Bearer <key>,不是x-api-key;最后确认 Key 没被控制台禁用。CI 里常见的是 secret 名字写错,比如 yaml 里写TAOTOKEN_KEY但 secret 存的是TAOTOKEN_API_KEY。
local proxy failed。现象是本地请求模型接口时报连接失败或代理错误。原因一般是本地网络环境有代理配置,或者HTTP_PROXY/HTTPS_PROXY环境变量指向了不可用的地址。处理:检查env | grep -i proxy,把不需要的代理变量 unset 掉再跑。CI 环境一般没有这个问题,本地开发机容易中招。
reading choices 报错。现象是解析模型返回时抛Cannot read properties of undefined (reading 'choices')。原因是返回体结构和预期不一致,可能是模型 ID 填错导致返回了错误对象,也可能是请求体格式不对。处理:先把原始返回console.log出来看结构,确认choices字段存在;再核对模型 ID 是否和模型对话页里试跑的一致。封装调用函数时加一层结构校验,返回体没有choices就抛出带原始响应的错误,方便定位。
OAuth 相关报错。如果你用的是 Claude Code 这类带 OAuth 流程的工具,报错通常出现在 token 刷新环节。现象是提示授权失效或回调失败。处理:确认回调地址和工具配置一致,重新走一次授权;如果工具支持直接填 API Key 模式,优先用 Key 模式,少一层 OAuth 就少一类问题。Claude Code 接入可以参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 里的说明。
Playwright 侧常见报错。expect(locator).toBeVisible() failed配合接口返回COUPON_STOCK_EMPTY,说明测试环境库存为空,属于测试数据初始化问题,不是脚本 bug。处理:在beforeEach里调测试数据准备接口重置库存,或者用独立的测试账号和测试活动。Timeout exceeded多半是定位器不稳或页面没加载完,优先换getByRole并加await expect(...).toBeVisible()显式等待,别用waitForTimeout硬等。
排查时把日志、接口返回、截图三样一起丢给 Codex,提示词:
你是一名测试开发,请分析下面 Playwright 自动化失败原因。 输出:失败现象、最可能原因、属于脚本/环境/产品缺陷哪一类、需要进一步确认的信息、建议修复方案。 测试日志:【粘贴】 接口返回:【粘贴】 截图描述:【粘贴】模型给的是初判,最终定性还是人来拍板。
6. 把流程沉淀下来:Skill 规范与后续接入
单次问答效率有限,真正省时间的是把团队规范做成可复用的 Skill。Codex 支持用SKILL.md描述任务规范,把测试点生成规则、Playwright 脚本规则、输出格式写进去,之后不用每次重复交代。
SKILL.md模板:
--- name: test-case-and-playwright description: 当需要根据需求生成测试点、测试用例或 Playwright 自动化脚本时使用。 --- # 测试用例与 Playwright 自动化规范 ## 一、测试点生成规则 必须覆盖:主流程、异常流程、边界值、权限校验、幂等性、数据一致性、异常恢复。 重点业务专项:订单(重复提交、状态流转、金额一致性)、优惠券(重复领取、库存扣减、券包一致性)、支付(失败、回调重复、金额校验)、权限(越权、资源归属)。 ## 二、Playwright 脚本规则 1. 统一使用 @playwright/test 2. 优先 getByRole、getByText、getByTestId,禁止 XPath 3. 环境地址、账号、密码、Token 全部从环境变量读取,禁止硬编码 4. 核心业务必须补接口/数据库数据断言,不依赖纯页面文案 5. 公共动作封装方法,代码复用 6. 兼容 CI 执行,无脏数据依赖 ## 三、输出格式 1. 测试点:Markdown 表格 2. 脚本:完整可运行代码 + 依赖说明 3. 附加:依赖测试数据、环境变量配置、不稳定风险点把这份SKILL.md放进项目,Codex 每次生成都会按规范走,团队输出标准统一。新人接手也不用从头讲一遍规则。
后续如果要接更多模型或工具,Key 还是走 TaoToken 那一个入口,Base URL 固定https://taotoken.net/api,换模型只改 Model ID。模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 可以先试跑确认模型可用,接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 查参数细节,API Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。长期跑编码和 Agent 任务的话,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 能把额度规划清楚。
最后留一个实操建议:每次生成脚本后,先跑一遍 Review 提示词,再本地跑通,最后才推 CI。三步顺序别省,省了哪步都会在 CI 里加倍还回来。测试数据隔离这件事越早做越好,等用例多起来再补,改造成本会高很多。