基于AI进行web界面自动化测试
一.项目背景随着数字化的不断推进问卷系统已成为企业、学术机构收集用户反馈、进行市场调研的核心工具。系统的 Web 界面功能日益丰富涵盖了问卷创建、题目编辑、逻辑跳转、数据导出等多个模块交互复杂度持续提升。传统的测试方式依赖于测试人员手动编写测试脚本不仅编写和维护成本高而且难以覆盖各种边界条件和异常场景。随着迭代加速回归测试的工作量呈指数级增长单纯依靠人力已无法满足快速交付与高质量保障的双重要求。为解决这一痛点本项目引入Web 界面 UI 自动化测试方案实现对问卷系统核心流程的无人值守回归。更进一步借助大语言模型的能力测试脚本的生成、维护、执行与结果分析全部由AI 接管测试人员只需用自然语言描述测试意图AI 即可自动完成用例设计、脚本生成、截图比对和报告输出真正实现从“写脚本”到“管流程”的转变显著提升测试效率与覆盖度。本系列文章将围绕以下维度展开UI 自动化测试的架构设计与技术选型AI 驱动测试用例生成与维护的实践全流程自动化的集成方案与落地经验。二.准备工作我们这里是基于codex来实现的我们前期的准备工作就是生成一个规定AI整体思路的文档要求其根据文档一个整体的思路来进行实现和操作模型使用的是最新的5.6 Sol这里codex还是十分强大的直接操控电脑预览了页面然后把所有页面都简单过了一下然后基于这个项目来生成一些要求和输出文档这是他生成的第一版说实话我有点不满意让其进行修改这是根据我的提示词修改的第二版最终版本这个版本基本符合我的需求虽然还有些小瑕疵但是也够用了。三.阶段 1文档与工程骨架我看了下他没按照建议的生成目录结构让他生成好目录结构打开vscode看更加直观可以看到这里已经完全生成好了可以提交了后面我进行的时候还是觉得不满意感觉整个流程还是很乱我删除仓库并重启对话重新开始现在使用计划模式并且提示词让其去调用使用浏览器的skills这样方便其更好的看到需要进行哪些方面的测试让他反问我的问题这样可以达到更好的效果问题1直接使用的推荐回答这个肯定选1因为这样才能模拟注册还有两个问题选1越深越好继续选1第一版生成的计划继续问的问题这里直接选3问了这么多问题终于可以开始了这样的方式AI还是比较谨慎的开始完成列举的TODO清单了他这里执行冒烟的时候其实也发现了我自己进行自动化测试的时候的错误就是有个页面会超时响应很久才有反应我看到执行了很久居然这里自己来跑所有任务千万注意不能这样不然会把上下文撑满这里看到他执行的一些用例算是比较基础的就是大框架的用例而不是去细分的所以SPEC.md也就是要求已经规定好了后我们直接可以进入到vscode中借助目前看到AI直接准备一把梭哈的时候已经快完成了第一阶段所以这里进行自检然后完成第二阶段四.阶段 2公共基础能力遇到的问题无头模式这里是因为我执行pytest时没有看到弹出对应的浏览器操作所以这里我问了一嘴延申出了这个问题需要观察操作过程时用有头模式执行python -m pytest --headed -s例如只运行登录模块python -m pytest tests/test_02_login.py --headed -s或运行单个用例python -m pytest tests/test_02_login.py::test_用例名称 --headed -s最推荐的调试命令是python -m pytest tests/test_02_login.py --headed -s -vv这里我直接修改了文件改成了有头模式如果后续想换成后台运行可以直接改成ture即可还补充了驱动管理放在哪里的检查了下五.阶段3注册提示词[SPEC.md](SPEC.md) 根据要求完成阶段3-注册先在此用例的基础上去完善 [测试用例.md](测试用例.md) 的注册部分先别急着写代码完善测试用例的时候按照当前格式并且先列一个大纲我看看哪些测试用例符合要求再去修改源文件阶段 3 主要完成内容本阶段围绕“注册”模块完成了文档、页面对象、数据驱动、自动化测试和账号初始化流程完善注册用例文档覆盖REG-001至REG-010根据真实页面规则补充注册数据 YAML完善RegisterPage页面对象表单字段识别字段级校验提示密码长度校验重复账号提示返回登录页面实现注册成功、登录验证和账号持久化增加运行时账号文件防覆盖机制增加--force-account-init显式强制初始化参数修改初始化脚本先执行校验用例再执行账号初始化增加账号信息和 Allure 页面源码的密码脱敏更新 README 和 pytest 标记测试过程中发现的 BUG1. 用户名约束的预期与实际页面不一致最初用例假设用户名存在最小长度、最大长度或字符格式限制。实际检查页面后发现用户名没有minlength没有maxlength没有pattern只有非空校验修复方式将用例调整为“用户名前端输入约束检查”自动化验证页面实际声明的约束属性为空不再臆测用户名长度或格式规则2. Selenium 没有捕获重复账号提示重复用户名提交后页面实际显示的是账号已存在但它不是普通的.ant-message-notice-content消息而是页面顶部的.ant-alert-message修复方式将页面对象的结果提示定位器扩展为.ant-message-notice-content, .ant-alert-message同时兼容普通消息提示和页面级错误提示3. 页面属性缺失时 Selenium 返回空字符串用户名没有pattern属性时Selenium 返回的是空字符串而 YAML 预期是null。修复方式element.get_attribute(pattern) or None对minlength、maxlength、pattern统一进行空值归一化最终返回None。4. 账号文件可能被重复覆盖原来的save_account()直接以写入模式打开runtime_account.yaml存在误覆盖长期测试账号的风险。修复方式默认禁止覆盖已有账号文件文件存在时抛出中文AccountStoreError只有显式传入overwriteTrue时才允许覆盖初始化脚本增加-Force参数用于明确执行账号替换5. 注册成功和登录验证存在执行顺序依赖原有文档将“注册成功”和“注册账号可登录”拆成两个依赖前一个用例的测试可能违反 pytest 不依赖文件顺序的要求。修复方式合并为完整的首次账号初始化流程生成唯一用户名注册登录验证保存runtime_account.yaml验证账号文件内容6. 敏感密码可能出现在失败产物中检查失败输出时发现pytest 失败参数可能直接展示账号字典中的密码Allure 页面源码可能包含密码输入框的value修复方式新增RuntimeAccount重写repr()只显示脱敏密码新增sanitize_page_source()对密码输入框的value替换为***Allure 附加页面源码前先执行脱敏测试结果最终验证结果阶段 1、阶段 2 基础测试24 passed注册模块全量测试12 passed, 1 skipped初始化脚本校验阶段12 passed初始化阶段因已有运行时账号按账号长期复用策略跳过config/runtime_account.yaml未被普通测试覆盖本阶段最终实现了注册模块从用例设计、页面操作、异常校验到首次账号初始化和安全持久化的完整流程。问题我自己写的时候没有将页面代码和测试代码分开还有个问题他把阶段测试也加在阶段测试中了提示词我我想最终在终端输入命令pytest的时候不去执行阶段性的基础测试这个在阶段结束的时候不是已经执行过了吗不需要重复啊这里pytest的默认配置时会把那个阶段性测试加上去如果后续后面自己执行pytest的时候会把那个阶段性测试加上去因为阶段性测试会在每个阶段已经跑完了那么后续不需要执行了只要把要求加在SPEC.md中执行阶段性测试的时候自己去执行这样阶段性测试不会浪费时间自己整体执行业务测试的时候也不会浪费时间六.阶段4登录这是他补充的用例还可以可以让他写入到测试用例.md中去了这里也是非常严谨啊修复了问题全部重新跑了一遍也是消耗了不少时间在这个修改和思考的过程中我们发现其实他也遇到了些问题这里我们让他总结在这个阶段发现了哪些问题问题主要遇到 4 个问题都是在跑真实登录页面时暴露出来的。conftest.py里登录失败终止逻辑一开始写错了语法我原本写成了pytest.fail(已保存账号登录失败终止业务测试不会自动注册新账号) from excpytest.fail()是函数调用不能接from exc所以 pytest 加载conftest.py时直接语法错误。解决方式改成显式抛AssertionError保留异常链raise AssertionError(已保存账号登录失败终止业务测试不会自动注册新账号) from exc登录后刷新页面侧栏容器出现了但菜单文本还没加载完LOGIN-009第一次失败时aside已经存在但aside.text是空字符串导致断言菜单缺失。原因是原来的LayoutPage.wait_loaded()只等侧栏元素可见没有等菜单内容渲染完成。解决方式把等待条件改成“侧栏可见且文本非空”再读取菜单文本self.wait( lambda driver: self._visible_side_menu_text(driver), 等待侧栏菜单加载完成, )这样刷新后不会太早断言。已登录状态访问登录页系统不会自动跳回/home我最初按测试用例预期理解为已登录后打开/user/login应该自动跳首页。真实站点不是这样它会停留在登录页但登录态没有丢。解决方式把该用例调整成更贴近真实行为已登录后打开登录页再访问受保护页面/home验证不需要重新登录菜单和账号状态仍正常。也同步更新了测试用例.md的 LOGIN-011 预期描述。页面上没找到明显“退出登录”入口我尝试点击右上角设置、头像、下拉触发器等但真实 DOM 和可见文本里没有稳定的“退出/退出登录”入口。页面登录态实际保存在localStorage.AuthorizationCookiesk-token部分 sessionStorage解决方式LayoutPage.logout()先尝试点击 UI 上的“退出/退出登录”如果找不到就清理登录态作为兜底退出localStorage.removeItem(Authorization) sessionStorage.clear() driver.delete_all_cookies() driver.get(/user/login)然后再访问受保护页面验证确实回到登录页。另外还有一个小稳定性问题LOGIN-001登录页控件检查在注册登录联跑时偶发失败因为core_controls_visible()原来用的是短等待。后来改成完整显式等待联跑就稳定通过了。这里第四点他其实说错了我们这个是有退出功能的只不过要点击展开页才能看到所以纠正一下他之前其实跑这个用例的时候为了过而过做了登录状态清除的兜底所以这是很不合理的这里要纠正其实是有这个元素的这里还是非常严谨每次修改完他都会检测有没有影响到整个项目的跑通我在这里还问了他关于元素定位他如何去定位的;七.阶段5普通业务模块这里涉及到的页面比较多11.我的项目页面有问题这里涉及到的主要问题是AI偷懒了有三个测试用例没有完成我让其完成的时候也出现了问题就是选择进入回收站时出现了一个确认弹窗但是他思考了却没有发现这个弹窗导致思考跑偏我看他在自动化运行测试用例的时候发现他根本没有处理好这个弹窗所以导致失败很多次所以这里及时进行了纠正这里他完成了写入但是还有一点不足的是我刚看了我的项目的测试用例比如说这里其实他没有主要到的是这里会跟设置按钮是一样的会进行展开选择这里提示他补充一下这里我让他自己操控浏览器来进行实际的体验看看如何进行测试用例的编写注意到这里居然还有未实现的偷懒了他这里出现了一个致命问题就是点击删除时其实有一个弹窗导致元素没有定位到导致一直在更改和思考直到我看到他的自动化的过程的时候发现其实他并没有点击到这个确定按钮这里我看到他思考的方向已经偏离了要及时纠正了这里我确实说的也没错我看他自动化的过程就看出来了他没有定位到确认按钮并且没有处理总结本阶段遇到的问题2.我的练习页面有问题P0这里因为是新账号没有我的练习的数据而我的练习需要去我的题库-新建这里必须要开启添加到我的练习题库才能在我的练习看到数据而这里他因为没有我的练习的数据导致很多用例直接跳过所以这里要跟他说一下要先在我的题库页面创建好题库然后点击新建注意这里告诉他必须选上这个添加到练习题库这样才能在我的练习页面看到后面他思考了很久一直在我的练习页面重复等待题库出现导致一直循环在执行那个测试用例所以我观察后立即中断后面更新好后发现另一个问题他创建完我的题库后进入我的练习然后又跳到了我的题库然后一直在等他创建的那条题库实则他创建的那条题库已经被挤到后面了需要点击下一页才能找到所有又在循环执行操作这里又中断然后进行提醒后面在我的练习页面点击开始练习出现了个弹窗他又进行了循环等待导致错误这里又中断然后重新告诉他怎么了这里我看他自动化跑的过程中一直卡在这个弹窗导致他一直卡在这个页面这里要重开一个对话了简单说明问题并修复这里我觉得的修复方案就是删除这4个测试用例因为我的题库页面创建时没有真实创建题目导致其实进入到我的练习页面并没有题目可供练习所有这里直接验证可以进入我的练习页面即可后面可以优化下 等我的题库创建完调换个执行顺序让其真正能跑后面的测试用例3.我的题库页面我们记得再每开始一个模块的时候记得看那个模块的测试用例是否符合我们的要求我们可以检查一下再去实现用例质量还可以可以让其进行实现这一次我全程没有干预 观察其完成效果感觉他这里遇到的问题还是比较多的等会来让他总结一下遇到了哪些问题又是如何进行修复的遇到的问题1补充问题就是我的题库可以命名两个同时存在的题库遇到的问题2我看测试用例中我的练习和我的题库页面的测试状态仅仅是已实现而不是已通过这样代表他可以只是实现了代码而并没有跑通整个流程 现在让他重新跑并修改状态这里因为他排查问题的时间会有点久我这里直接给他一个流程操作引导一下 这样就完成了任务4.补充说明这里问题管理和我的笔记就涉及到了我的题库和我的练习的链路因为这里不涉及那么复杂的操作如果感兴趣你们可以作为拓展实现我这里相当于只对主流程进行了自动化测试这里如果要实现的话就要先补充我的题库进行题目的创建还不能仅仅只是创建空题库还要必须创建一个有题目的题库 那么这样的话涉及到的测试用例又有很多所有有些复杂并且你创建完有题目的题库还必须要进行练习不进行练习的话是没有问题管理和我的笔记的所以这里我并没有实现的这么复杂只做了简单的主流程 感兴趣的话可以进行拓展和优化5.模板广场单独执行某一模块的命令注意提示词[SPEC.md](SPEC.md) [测试用例.md](测试用例.md) 根据要求和测试用例完成模板广场部分的自动化代码的编写这里注意以下问题1.选择一个可用分类进行筛选时 这里有公共库和私有库区分这个的话是要在添加模板时勾选是否添加到公共库 如果不勾选你要先点击私有库才能看到你创建的分类 我的建议是调整一下测试用例的顺序和实现顺序先把创建模板放到首位 这样下面的测试用例才能更好的实现2.添加模板的话在模板广场首页的有添加模板的按钮 点击会跳转到添加模板的详情页面 这里添加模板的话不做其他复杂的操作 等待加载完毕后直接点击保存会弹出**保存到问卷模板库的弹窗如图一 这里还要点击保存 点击完保存后 其不会自动跳转到模板广场首页这里你要自行去点击关闭按钮才会返回到模板广场首页**3.预览弹窗的话是把鼠标定位到你刚刚创建的模板上然后下面会出现导航栏第三个像电脑图标的那个 移动到上面会出现预览模板的提示 点击才能完成预览操作4.你删除模板的测试用例的步骤写的有问题 不用去跳转到我的项目页面 也是在模板广场首页鼠标定位到你刚刚创建的模板上然后下面会出现导航栏 第一个就是删除 点击删除后还会有确认弹窗5.注意一定要把创建模板放到首位整个自动化流程中使用你创建的那一个模板并记录名称 后续都是用它 正好最好一个测试用例是删除创建的模板根据上面的注意点先修改测试用例再执行这里我不让他直接进行自动化测试代码的编写因为我看了测试用例和真正进入到网页进行操作发现很多不一样的点然后对其进行补充和修改现在这样详细描述的他的效果还是比之前直接进行代码的实现操作要准确不少这里总结一下几个问题问题1这里我告诉点击添加模板后是弹窗一个新的页面这里AI以为是更换了baseURL他根据baseurl来等待页面处理的出现然后定位保存按钮实际上点完添加模板直接弹出了个编辑器并且有切换页面也就是baseburl并没有切换导致这里他并没有定位到保存按钮导致失败这里引导了一下就成功解决了 实际上并没有切换页面他这里却一直等待新页面出现导致失败问题2填写分类时没有定位到分类输入框就直接输入导致添加模板用例执行失败问题3最严重无法搜索到已经保存的模板这里我让AI去重新执行然后去搜索他一开始找出的问题是他觉得的问题可能是后端做了校验就是如果模板里面没有东西是搜索不到的 然后我这自行手动去创建了一个有内容的模板还是搜索不到 然后我就及时打断觉得这里是网站的原因然后这里让他重新调整 并告诉他我的决策就是删除涉及到根据模板名称搜索的这个测试用例之前他校验创建成功模板时 他也是想根据是否搜索的到来完成 但是其实这里这个功能是错误的导致执行一直卡在这里然后失败这里正好创建成功会有一个提示模板创建成功的提示弹窗我让他根据这个来校验模板创建成功这里总结了几点网站为什么会发生这种情况的BUG我会按概率这样判断保存接口返回成功但数据没有真正写入数据库后端可能先返回成功消息但事务失败、异常被吞掉或者保存的是临时编辑数据而不是模板记录。保存成功但模板关联的用户/租户 ID 不正确私有库查询通常会按当前用户过滤。如果保存时没有正确写入user_id、owner_id或租户字段创建接口能成功查询接口却永远查不到。保存接口和查询接口使用了不同的数据表或状态字段例如保存写入“问卷模板表”查询只查“已发布模板表”或者保存状态是draft查询只筛选published/active。私有库查询接口的参数或权限条件错误比如前端传了错误的libraryType、用户身份参数或者后端把私有库错误地按公共库逻辑查询。保存成功提示只是前端乐观提示前端可能在请求发出后就弹出“保存成功”没有根据真实后端响应判断。请求实际可能返回 4xx/5xx但错误没有展示出来。查询接口本身有问题搜索关键词可能没有正确传给后端或者接口固定返回空数组。可以通过不输入关键词、切换公共库/私有库来验证。这个阶段结束结束后我pytest执行了一下以上所有的测试用例这个21deselected是没有被选中的意思 因为他是阶段性测试已经测阶段1和阶段2已经完成测试了没有必要再进行测试从而浪费时间八.阶段6系统管理模块因为组织机构需要管理员才能进行编辑 那么我们没有管理员权限 所以这里只能进行普通账户的流程测试 那么这里直接去掉有关组织机构的代码和测试用例1.岗位设置这里的测试用例有问题应该先执行新建岗位再去查询 然后再去修改相关的数据还有使用重复编码不会报错纠正一下问题下拉框没有定位到导致创建失败实际上是因为编码过长导致失败页面也没有给出提示这里提示词写的很清楚了不懂为什么还是定位不到这个元素我这里重开一个对话来让其进行修复这里他一直修复不好 我想的是就把他弄的文本填入进去我自己点击试一试发现 是岗位编码过长导致点击了也没有效果这个页面居然没有做提示导致测试用例失败一直卡在这块问题2由于创建的岗位过多点击确认后没有看到那个岗位是因为在分页的后面问题三担心分页过多执行了删除操作但这里他为了避免分页过多又自行加上了删除逻辑 完全没有必要 这里告诉他让他重新修复问题四这里要做一个修改就是后续的操作只读创建时所生成的岗位这次岗位设置主要遇到了以下问题测试数据没有串联每个用例都重新生成岗位名称。编辑、删除无法复用创建用例生成的岗位。现在通过config/runtime_position.yaml保存岗位名称、编码和权限后续用例统一读取删除成功后清理该文件。编辑逻辑改错字段原编辑用例修改的是岗位名称。已调整为保持岗位名称和数据权限不变只修改岗位编码。新编码缩短为POS_加 6 位随机字符避免页面长度校验导致保存失败。删除确认弹窗没有等待原逻辑直接查找并点击确定按钮弹窗还未加载时会定位失败。现在会等待包含“删除”和“确定”的可见弹窗再定位弹窗内部确定按钮点击后继续等待弹窗关闭。岗位名称过长时列表显示省略号Selenium 的row.text只能拿到截断后的名称导致明明存在岗位却判断为不存在。现在同时读取单元格的title和textContent可以匹配完整名称。Ant Design 受控输入框清空异常查询框使用clear()后再输入时旧值可能没有同步清除导致新旧名称拼接。现在优先使用CtrlA后输入确保 React 表单状态同步。编辑抽屉编码输入框不可直接交互编码输入框在页面布局中有时被压缩Selenium 直接clear()会报ElementNotInteractableException。已增加原生输入值设置及input/change/blur事件触发逻辑。编辑抽屉确定按钮位于表单外新建和编辑抽屉的按钮结构不完全一致编辑按钮位于抽屉页脚。提交逻辑现在会同时查找表单内按钮和抽屉/模态页脚按钮。重复编码用例误删主岗位重复编码用例清理第二个岗位时之前也可能把后续用例需要的主岗位删掉。现在只删除重复创建的第二个岗位保留主岗位直到最终删除用例处理。本地基础测试、岗位关键用例以及你重新执行的完整岗位模块测试均已通过。后面我又运行时还有错误又进行了修复这次岗位设置问题主要集中在“测试数据串联、页面异步渲染、Ant Design DOM 结构差异”三个方面。1. 创建后的岗位没有被后续用例复用原因每个测试都用unique_value()生成新的岗位名称创建、查询、编辑、删除之间没有共享数据。修复新增utils/position_store.py。创建成功后写入config/runtime_position.yaml。查询、编辑、重复编码、删除用例优先读取这份数据。删除成功后清理运行时岗位文件。单独运行后续用例时如果运行时数据不存在会自动补建岗位。2. 编辑用例修改了错误字段原因原来的edit_position()修改的是岗位名称和实际需求不符。修复编辑时保持岗位名称不变。只修改岗位编码。数据权限也保持原值不变。新编码缩短为POS_加 6 位随机字符总长度为 10 位避免页面长度校验失败。3. 删除确认弹窗定位不稳定原因删除弹窗是异步加载的并且通常通过 Portal 挂载到页面外层。原逻辑立即查找“确定”按钮弹窗尚未出现时就会定位失败。修复等待包含“删除”和“确定”的可见弹窗出现。在弹窗内部查找“确定”按钮。点击后继续等待弹窗关闭。增加ant-btn-primary作为按钮定位兜底。4. 长岗位名称导致“岗位不存在”误判原因表格名称列使用了省略号Selenium 的row.text只能拿到页面上显示的截断文本而不是完整名称。修复同时读取表格单元格的title和textContent。表格重绘期间 Selenium 暂时获取不到行时再通过 JavaScript 从当前 DOM 查询完整名称。position_visible()在已经找到匹配行时直接返回成功减少重复查询造成的时序问题。5. 查询框出现旧值和新值拼接原因Ant Design 的查询框是 React 受控组件直接执行clear()后再send_keys()原生输入框的值清除了但 React 状态没有同步更新重新渲染时旧值又被恢复。修复对可交互输入框使用CtrlA后输入。对不可交互输入框使用原生valuesetter并触发input/change/blur事件让 React 状态同步。6. 编辑抽屉的编码输入框不可直接操作原因编辑抽屉中的编码输入框在某些窗口布局下被压缩虽然存在于 DOM 中但实际尺寸不可交互直接clear()会触发ElementNotInteractableException。修复优先使用真实键盘输入。输入框不可交互时使用 JavaScript 原生 setter 更新值并触发输入事件。7. 编辑抽屉的确定按钮不在表单内部原因新建抽屉和编辑抽屉的 DOM 结构不同。编辑页面的“确定”按钮位于.ant-drawer-footer是表单的兄弟节点不能只在表单内部查找按钮。修复先查找表单内部按钮。找不到时查找抽屉或模态框页脚按钮。最后再按按钮文本“确定”进行全局兜底定位。8. 重复编码用例误删主岗位原因重复编码用例清理时曾经把第二个岗位和主岗位都删除导致后面的删除用例找不到创建用的岗位。修复重复编码用例只删除第二个岗位。主岗位一直保留到最终删除用例。9. 配置文件拼写错误原因config/config.yaml中曾出现headless: ture配置解析会把未知字符串当成false导致实际使用有头浏览器。修复为headless: true最终验证结果岗位模块6 passed 基础阶段20 passed这些问题本质上是测试状态共享、React 受控组件、Ant Design 异步渲染和虚拟表格文本截断共同造成的。2.个人设置九.最终验收并生成测试报告Allure 报告完整执行步骤在项目根目录依次执行1. 执行测试并生成 Allure 原始数据python -m pytest --clean-alluredir原始数据生成在reports/allure-results2. 生成 HTML 报告allure generate reports/allure-results -o reports/allure-report --cleanHTML 报告生成在reports/allure-report3. 启动本地服务器并打开报告allure open reports/allure-report浏览器会打开类似下面的地址http://127.0.0.1:端口号/运行报告期间不要关闭这个 PowerShell 窗口。完整命令汇总python -m pytest --clean-alluredir allure generate reports/allure-results -o reports/allure-report --clean allure open reports/allure-report也可以省略“生成 HTML 报告”这一步测试结束后直接临时启动报告服务器python -m pytest --clean-alluredir allure serve reports/allure-results十.总结整个的项目架构与流程pytest Selenium Page Object YAML 数据驱动 AllureWeb UI 自动化测试项目。核心设计是把“测试数据、页面操作、测试逻辑、运行配置”拆开管理。整体架构执行脚本 scripts/ │ ▼ pytest.ini 选择测试范围、优先级 │ ▼ conftest.py 创建浏览器、准备账号和登录状态 │ ▼ tests/test_xxx.py 组织测试步骤和断言 │ ├──读取──▶ data/xxx.yaml │ └──调用──▶ pages/xxx_page.py │ ▼ Selenium 操作网页 │ ▼ 实际系统页面各目录职责目录/文件主要作用data/保存测试输入、预期结果、用例编号pages/封装页面元素定位和页面操作tests/编写真正的测试流程和断言utils/YAML、配置、日志、账号、随机数据等公共工具config/保存运行环境、浏览器配置和运行时数据scripts/提供冒烟、回归、报告生成等执行入口conftest.pypytest 公共 fixture 和失败处理pytest.inipytest 的收集、标记、报告配置reports/Allure 测试结果和报告logs/自动化运行日志YAML 文件是干什么的项目中实际上存在三类 YAML。1. 测试数据 YAML例如 [exercise.yaml](D:/AI测试项目/AIweb自动化/data/exercise.yaml)cases: - case_id: EXERCISE-001 title: 打开我的练习 priority: P0 enabled: true data: {} expected: path: /exercise menu: 我的练习主要字段case_id测试用例唯一编号title用例名称priority业务优先级enabled是否加载这条用例data测试输入expected预期结果测试文件通过 [yaml_loader.py](D:/AI测试项目/AIweb自动化/utils/yaml_loader.py) 读取CASES { case[case_id]: case for case in load_cases(root_path(data, exercise.yaml)) }然后根据编号使用case CASES[EXERCISE-001] assert case[expected][menu] in page.menu_text() assert case[expected][path] in driver.current_url所以你的理解是对的data/*.yaml用来提供测试数据和预期结果。不过当前项目不是完全参数化的数据驱动。它采用的是测试代码主动根据case_id读取对应 YAML 数据。也就是说YAML 不会自动生成所有测试函数而是为已有测试函数提供数据和预期值。enabled: false的用例会在加载阶段被过滤return [case for case in cases if case.get(enabled, True)]2. 环境配置 YAML[config.yaml](D:/AI测试项目/AIweb自动化/config/config.yaml) 保存运行配置base_url: http://49.235.61.184:8080 browser: name: chrome headless: true window_size: 1440,1000 timeouts: explicit: 20 page_load: 30它控制测试网站地址浏览器类型是否无头运行浏览器窗口尺寸Selenium 等待时间日志和报告目录由 [config_loader.py](D:/AI测试项目/AIweb自动化/utils/config_loader.py) 读取。环境变量可以覆盖其中部分配置例如$env:TEST_BASE_URL http://其他地址 $env:TEST_HEADLESS false3. 运行时 YAML例如config/runtime_account.yaml config/runtime_position.yaml它们不是固定测试用例而是保存自动化运行过程中产生的数据。runtime_account.yaml保存首次注册成功的账号后续测试重复使用runtime_position.yaml保存自动化创建的岗位供其他用例继续操作或清理。pages 目录是干什么的pages/使用的是 Page Object 页面对象模式。它的核心思想是页面怎么操作写在 Page 类里测试要验证什么写在 tests 里。例如 [exercise_page.py](D:/AI测试项目/AIweb自动化/pages/exercise_page.py) 中定义NAME_INPUT (By.CSS_SELECTOR, input#name) QUERY_BUTTON (By.XPATH, //button[normalize-space()查询])以及页面动作def search_by_name(self, name: str) - None: self.fill(self.NAME_INPUT, name) self.click(self.QUERY_BUTTON) self.wait_query_finished()测试文件不需要知道输入框的 CSS、按钮 XPath也不需要重复编写等待逻辑只需要page.search_by_name(repository_name) assert page.exercise_visible(repository_name)这样网页定位器发生变化时一般只修改 Page 文件不需要修改所有测试。Page 类继承关系BasePage │ ├── LoginPage ├── RegisterPage │ └── LayoutPage │ └── RoutePage │ ├── ProjectPage ├── ExercisePage ├── RepositoryPage ├── TemplatePage ├── PositionPage └── SettingPage[base_page.py](D:/AI测试项目/AIweb自动化/pages/base_page.py) 提供最基础的 Selenium 能力打开页面查找元素点击元素输入内容显式等待获取文本等待 URL截图LayoutPage增加登录后公共布局能力等待侧栏菜单获取菜单文字退出登录清理登录状态RoutePage增加业务模块页面的公共能力打开并验证页面获取表格表头点击按钮打开弹窗按占位符搜索具体的ExercisePage、ProjectPage再实现各自业务动作。tests 目录是干什么的tests/负责把下面三部分组合起来测试前置条件Page Object 页面操作测试断言例如 [test_04_exercise.py](D:/AI测试项目/AIweb自动化/tests/test_04_exercise.py)def test_exercise_page_accessible(logged_in_driver): case CASES[EXERCISE-001] page open_exercise_page(logged_in_driver) assert case[expected][menu] in page.menu_text() assert case[expected][path] in logged_in_driver.current_url assert page.core_area_visible()其中logged_in_driver由conftest.py提供CASES从data/exercise.yaml读取ExercisePage封装页面操作assert真正判断测试是否通过这也是项目的职责边界YAML输入什么、期望什么 Page页面怎么操作 Test执行什么步骤、验证什么结果conftest.py 如何连接测试[conftest.py](D:/AI测试项目/AIweb自动化/conftest.py) 是整个测试运行的公共入口。driverfixture 每条测试创建一个 Chromepytest.fixture def driver(request): web_driver webdriver.Chrome(optionsoptions) yield web_driver web_driver.quit()registered_account读取已经保存的测试账号pytest.fixture(scopesession) def registered_account(): return load_account()logged_in_driver在测试开始前自动登录pytest.fixture def logged_in_driver(driver, registered_account): page LoginPage(driver).open() page.login( registered_account[username], registered_account[password], ) return driver测试只要把 fixture 名写到参数里pytest 就会自动执行def test_exercise_page_accessible(logged_in_driver):调用过程是pytest 发现测试需要 logged_in_driver │ ├──创建 driver ├──读取 registered_account ├──使用 LoginPage 登录 ▼ 把登录后的 driver 传给测试函数一条练习测试的完整调用链以EXERCISE-001为例run_smoke.ps1 │ ▼ pytest -m smoke and not stage │ ▼ 找到 test_exercise_page_accessible │ ▼ conftest.py 创建 Chrome │ ▼ 读取 runtime_account.yaml │ ▼ LoginPage.login() 完成登录 │ ▼ test_04_exercise.py 读取 EXERCISE-001 │ ▼ ExercisePage.open_and_verify() │ ▼ BasePage.open() 打开 /exercise │ ▼ Selenium 操作实际网页 │ ▼ 测试根据 YAML expected 执行 assert │ ├──通过写入 Allure 结果 └──失败附加截图、URL、源码和错误摘要scripts 和 pytest 标记[pytest.ini](D:/AI测试项目/AIweb自动化/pytest.ini) 定义了两类标记。按优先级p0核心功能 p1主要业务 p2边界和异常按用途或模块smoke regression login project exercise repository template position setting[run_smoke.ps1](D:/AI测试项目/AIweb自动化/scripts/run_smoke.ps1) 执行python -m pytest -m smoke and not stage只运行带smoke标记的核心测试。[run_regression.ps1](D:/AI测试项目/AIweb自动化/scripts/run_regression.ps1) 依次执行P0 → P1 → P2即使 P0 失败也会继续运行 P1 和 P2最后统一返回失败状态。总结成一句话scripts决定运行哪些测试conftest.py准备浏览器和账号tests组织业务流程data提供数据和预期结果pages负责操作页面utils提供公共基础能力最后由 Allure 和日志记录执行结果。