
1. 为什么前端表单测试总在“最后一公里”翻车前端表单测试的痛点做过的人都懂一个注册页七八个字段下拉框、复选框、日期选择器、文件上传全凑齐了手动点一遍要三分钟改一行代码又得从头再来。用 Selenium 写脚本吧元素定位、显式等待、iframe 切换、动态 class 名随便一个坑就能让脚本红一片。更麻烦的是这些脚本往往只有写的人能维护换个人接手光看懂定位逻辑就要半天。Codex Skills 想解决的正是这个问题。它把“模拟人工点击、填写、断言”这套动作封装成一个可复用的 SkillAI 负责理解自然语言指令并生成调用参数Selenium 负责底层执行。你不再需要每次从零写脚本而是配置一次 Skill 骨架之后换页面、换数据、换断言条件改几个参数就能复跑。适合谁适合正在做前端自动化测试、又不想被脆弱脚本绑架的开发者也适合想把 AI Agent 接入测试流程的团队。我试过把一套多步表单用例从手写脚本迁移到 Skill 驱动最大的感受是定位策略和等待逻辑被收敛到了 Skill 内部业务侧只需要描述“填什么、点什么、验什么”。下面把可复制的配置骨架、Selenium 脚本片段和本地验证步骤完整拆开讲。2. 前置准备TaoToken 接入与 Skill 运行环境Skill 要驱动 AI 理解指令并生成调用参数需要一个稳定的模型接入层。TaoToken 在这里扮演的是统一 API 入口的角色你不需要在代码里硬编码某一家模型的地址和密钥换模型时只改配置不改业务逻辑。先拿到 API Key。访问 https://taotoken.net/api-keys 创建密钥建议按项目分 key方便后续排查调用来源。创建后在控制台 https://taotoken.net/console 能看到用量和调用记录。接入文档在 https://taotoken.net/doc 里面覆盖了对话补全、函数调用Function Calling的请求格式。Skill 驱动表单测试的核心就是让模型输出结构化的函数调用参数所以重点看 Function Calling 那一节。如果你打算长期跑编码类 Agent 任务比如让 Skill 自动生成并维护测试脚本可以了解 Coding Planhttps://taotoken.net/coding-plan 。它更适合高频、长会话的编码场景普通表单测试用按量调用就够。环境侧需要 Python 3.9、Chrome 浏览器、以及 Selenium 和 webdriver-manager。webdriver-manager 会自动匹配 Chrome 版本下载驱动省掉手动对版本的麻烦。python -m venv skill_env source skill_env/bin/activate # Windows 用 skill_env\Scripts\activate pip install selenium webdriver-manager requests注意Chrome 浏览器版本和驱动版本必须匹配webdriver-manager 会自动处理但如果你的环境无法访问外网下载驱动需要提前把对应版本的 chromedriver 放到 PATH 里。3. 可复制配置Skill 骨架与 Selenium 执行层3.1 Skill 配置骨架Skill 的本质是一份 JSON Schema 加一段执行逻辑。Schema 告诉模型“这个 Skill 需要哪些参数”执行逻辑负责真正操作浏览器。下面这份骨架可以直接复制改掉 URL 和字段定位即可。{ name: form_test_skill, description: 驱动浏览器模拟人工填写并提交前端表单支持文本输入、下拉选择、复选框勾选和结果断言。, parameters: { type: object, properties: { url: { type: string, description: 目标表单页面地址 }, fields: { type: array, description: 待填写的字段列表, items: { type: object, properties: { locator: { type: string, description: CSS 选择器或 XPath }, value: { type: string, description: 填入的值 }, action: { type: string, enum: [input, select, check, click] } }, required: [locator, action] } }, assert_text: { type: string, description: 提交后页面应包含的文本 } }, required: [url, fields] } }这份 Schema 的关键设计是fields用数组承载多步操作action枚举区分输入、下拉、勾选、点击。模型拿到自然语言指令后会把它翻译成这个结构Skill 执行层再逐条执行。3.2 Selenium 执行层执行层负责把 Schema 里的参数变成真实操作。核心是显式等待加异常截图避免页面没加载完就操作导致误报。import os, time, json from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait, Select from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException from webdriver_manager.chrome import ChromeDriverManager class FormTestSkill: def __init__(self, headlessTrue, shot_dir./shots): self.shot_dir shot_dir os.makedirs(shot_dir, exist_okTrue) opts Options() if headless: opts.add_argument(--headlessnew) opts.add_argument(--disable-gpu) opts.add_argument(--window-size1920,1080) opts.add_argument(--disable-dev-shm-usage) self.driver webdriver.Chrome( serviceService(ChromeDriverManager().install()), optionsopts ) self.wait WebDriverWait(self.driver, 15) def run(self, url, fields, assert_textNone): self.driver.get(url) for f in fields: el self.wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, f[locator])) ) act f[action] if act input: el.clear() el.send_keys(f[value]) elif act select: Select(el).select_by_visible_text(f[value]) elif act check: if not el.is_selected(): el.click() elif act click: el.click() if assert_text: try: self.wait.until(EC.text_to_be_present_in_element( (By.TAG_NAME, body), assert_text)) return {success: True} except TimeoutException: self.shot(assert_fail) return {success: False, reason: 断言文本未出现} return {success: True} def shot(self, name): path os.path.join(self.shot_dir, f{name}_{int(time.time())}.png) self.driver.save_screenshot(path) return path def close(self): self.driver.quit()这段代码里几个细节值得说--headlessnew是新版无头模式比旧版兼容性好--disable-dev-shm-usage在容器里能避免共享内存不足导致崩溃element_to_be_clickable同时等可见和可点击比单纯等可见更稳。3.3 让模型生成调用参数把 Schema 和用户指令一起发给模型让它输出 JSON。这里用 TaoToken 的对话接口请求体里带上 tools 定义。import requests resp requests.post( https://taotoken.net/api/v1/chat/completions, headers{Authorization: Bearer YOUR_API_KEY}, json{ model: gpt-4o-mini, messages: [{role: user, content: 测试注册页用户名填 tester邮箱填 texample.com密码填 Pass1234国家选 China勾选同意条款点提交验证页面出现 Welcome}], tools: [{type: function, function: SKILL_SCHEMA}] } ) print(resp.json()[choices][0][message][tool_calls])模型返回的tool_calls里就是填好的fields数组直接喂给FormTestSkill.run()即可。这样业务侧只写自然语言参数生成交给模型。4. 验证请求本地跑通一次多步表单用例先准备一个本地测试页避免依赖外部站点。建一个form.html包含用户名、邮箱、密码、国家下拉、同意复选框和提交按钮提交后用 JS 在页面插入 “Welcome” 文本。!DOCTYPE html htmlbody input idusername placeholder用户名 input idemail placeholder邮箱 input idpassword typepassword placeholder密码 select idcountryoptionChina/optionoptionUS/option/select labelinput typecheckbox idagree 同意条款/label button idsubmit onclickdocument.body.insertAdjacentHTML(beforeend,pWelcome/p)提交/button /body/html用 Python 起个静态服务python -m http.server 8000然后调用 Skillskill FormTestSkill(headlessFalse) result skill.run( urlhttp://localhost:8000/form.html, fields[ {locator: #username, value: tester, action: input}, {locator: #email, value: texample.com, action: input}, {locator: #password, value: Pass1234, action: input}, {locator: #country, value: China, action: select}, {locator: #agree, value: , action: check}, {locator: #submit, value: , action: click}, ], assert_textWelcome ) print(result) skill.close()成功时输出{success: True}页面截图会保存在./shots下。如果断言失败会返回失败原因并自动截图方便定位是哪个字段没填对。实测下来这套流程跑通后换一个表单页面只需要改fields里的定位器和值Skill 本身不用动。对于多步表单比如先填基本信息、再填地址、最后确认提交把fields数组按顺序排好即可执行层会逐条等待并操作。5. 本篇常见错排查定位不到元素报 TimeoutException。最常见的原因是选择器写错或元素在 iframe 里。先用浏览器开发者工具的 Console 执行document.querySelector(#username)确认选择器有效如果在 iframe 内需要先driver.switch_to.frame()。另外动态渲染的页面元素可能在请求返回后才出现显式等待能覆盖大部分情况但等待时间要设够。下拉框选择失败报 UnexpectedTagNameException。Select类只能用于原生select标签。如果页面用的是自定义下拉组件比如 div 模拟的需要先点击触发展开再点击选项不能用Select。判断方法在 Console 里看该元素 tagName 是不是 SELECT。复选框点击后没生效。有些复选框被 label 包裹点击 label 也能触发但直接点 input 可能被遮挡。用element_to_be_clickable等待可点击如果仍失败可以改用driver.execute_script(arguments[0].click(), el)强制点击。无头模式下截图空白或布局错乱。设置--window-size1920,1080固定视口避免默认窗口过小导致响应式布局把元素藏起来。如果截图仍空白检查是否在页面加载完成前就截图加一个等待标题或关键元素出现的逻辑。模型返回的参数格式不对。检查 Schema 里的required和enum是否写清楚模型对枚举值敏感。如果模型把action写成中文或拼错可以在执行层加一层校验遇到非法 action 直接报错并打印原始参数方便调试。调用 API 返回 401 或 403。检查 API Key 是否正确、是否过期以及请求头格式是否为Bearer YOUR_API_KEY。如果是在 CI 环境跑确认环境变量注入成功不要把 key 硬编码进仓库。6. 把 Skill 接进你的测试流程跑通单次用例后下一步是让它可复跑。把fields配置抽成 JSON 文件每个表单用例一个文件Skill 读取文件执行。这样新增用例就是新增一个 JSON不用改代码。断言部分可以扩展成多个条件比如同时验证文本和某个元素消失。如果要在 CI 里跑把headlessTrue打开截图目录挂到构建产物里失败时直接看截图定位问题。模型调用那一步可以缓存结果同一套指令不必每次都请求减少 token 消耗。需要长期维护测试脚本、让 Agent 自动生成和更新用例的可以走 Coding Plan长会话下更划算。日常调试和验证模型输出用模型对话页面快速试指令即可。接入细节和 Function Calling 的完整参数说明文档里都有对应示例照着改就能用。