:Checkout 信息页测试与分层执行)
从 0 到 1 搭建 Selenium Pytest Page Object 自动化测试框架实战三Checkout 信息页测试与分层执行在上一篇中项目完成了商品加入购物车、购物车数据一致性、删除、刷新与结算跳转等测试。本篇继续推进到 Checkout Information 页面验证用户信息填写、必填校验、边界输入并把测试组织方式升级为“目录分模块 Marker 按标签执行 统一运行入口”。测试网站https://www.saucedemo.com/一、本阶段完成了什么从购物车点击Checkout只代表进入了结算流程真正值得验证的是用户信息能否提交、必填字段是否有明确提示、特殊输入在当前站点中如何表现以及这些不同类型的用例能否被灵活执行。本阶段新增的能力如下模块本阶段实现内容价值CheckoutInformationPage封装姓名、邮编、Continue、Cancel 与错误提示将结算信息页操作集中管理checkout_pagefixture自动完成登录、加购、进入购物车、点击 Checkout用例只关注本页测试目标参数化测试覆盖三个必填字段缺失场景减少重复代码统一断言逻辑边界测试空格、Tab、特殊字符、超长名称记录页面真实输入行为Pytest Markersmoke、negative、boundary支持按测试类型筛选执行run_tests.py提供模块、标签和全量测试入口降低命令记忆与执行成本当前项目静态收集到27 条测试用例登录 1 条、商品列表页 6 条、购物车 8 条、Checkout 信息页 12 条。图 1 Checkout Information 页面核心元素二、项目结构按业务模块组织测试随着页面和用例增加把所有测试文件平铺在一个目录中会越来越难维护。最新版本按照业务模块重新组织了测试目录SWAG_Lags_Autotest/ ├── base/ │ └── base_page.py ├── pages/ │ ├── login_page.py │ ├── inventory_page.py │ ├── cart_page.py │ └── checkout_information_page.py ├── services/ │ └── inventory_service.py ├── testcases/ │ ├── inventory/ │ │ └── test_inventory.py │ ├── cart/ │ │ └── test_cart.py │ ├── checkout/ │ │ └── test_checkout.py │ └── test_login.py ├── conftest.py ├── pytest.ini └── run_tests.py这种结构的重点不是“目录越多越好”而是让测试范围与业务页面一一对应想执行购物车模块时定位到testcases/cart想验证结算页时定位到testcases/checkout。对于 Checkout 用例前置路径已经由 fixture 统一完成登录标准用户 ↓ 商品列表页添加商品 ↓ 进入购物车 ↓ 点击 Checkout ↓ 进入 Checkout Information 页面 ↓ 执行当前测试用例图 2 进入 Checkout 前的购物车状态三、CheckoutInformationPage页面对象只负责页面行为Checkout Information 页面包含三个输入框、两个操作按钮和一个错误提示区域。页面对象中统一定义定位器并提供语义化方法classCheckoutInformationPage(BasePage):first_name_input(By.ID,first-name)last_name_input(By.ID,last-name)postal_code_input(By.ID,postal-code)cancel_button(By.ID,cancel)continue_button(By.ID,continue)error_message(By.CSS_SELECTOR,[data-testerror])1. 将“填写”和“提交”拆开页面对象没有把所有逻辑塞进一个方法中而是把“填写表单”与“提交表单”区分开deffill_checkout_information(self,first_name,last_name,postal_code):self.enter_first_name(first_name)self.enter_last_name(last_name)self.enter_postal_code(postal_code)defsubmit_checkout_information(self,first_name,last_name,postal_code):self.fill_checkout_information(first_name,last_name,postal_code)self.click_continue()这样做有两个好处正向、负向和边界用例都可以复用同一个提交入口后续如果需要验证“填写后不提交”“点击 Cancel”等交互不必再拆分原有方法。2. 错误信息由页面对象读取defget_error_message(self):returnself.get_text(self.error_message)测试层不需要直接写 CSS Selector也不用自行判断错误元素是否已经加载它只需要验证最终可见的业务提示。四、fixture把重复的前置步骤收回到测试环境中Checkout 的每条测试都依赖同一条前置业务链路。如果每个用例都手动写登录、加购、跳转购物车和点击 Checkout测试代码会非常冗长而且任何流程变更都需要修改多处。因此新版本在conftest.py中提供了checkout_pagefixturepytest.fixturedefcheckout_page(logged_in_driver):inventory_pageInventoryPage(logged_in_driver)cart_pageCartPage(logged_in_driver)inventory_page.add_product_by_index(0)inventory_page.go_to_cart()cart_page.click_checkout()returnCheckoutInformationPage(logged_in_driver)使用 fixture 后测试用例的关注点变得更清晰deftest_checkout_information_submit_success(driver,checkout_page:CheckoutInformationPage):checkout_page.submit_checkout_information(Jack,Smith,114514)assertcheckout-step-two.htmlindriver.current_url测试代码不再描述“如何到达页面”而是直接描述“到达页面之后需要验证什么”。这也是 fixture 在 UI 自动化中最实用的价值。五、Checkout 测试设计正向、负向与边界输入结算信息页当前共覆盖 12 条用例类型场景断言目标冒烟三项信息完整填写进入checkout-step-two.html负向缺少 First Name提示First Name is required负向缺少 Last Name提示Last Name is required负向缺少 Postal Code提示Postal Code is required边界First Name 为一个或多个普通空格记录当前站点接受空格输入的行为边界First Name 为 Tab 字符提示 First Name 必填边界First Name 为特殊字符当前站点允许进入下一步边界First Name 长度为 256当前站点允许进入下一步负向First Name、Last Name 同时缺失优先提示 First Name 必填负向三项字段全部为空优先提示 First Name 必填负向不填写任何信息直接点击 Continue提示 First Name 必填1. 用参数化覆盖同类必填校验三个字段缺失的处理逻辑相同区别仅在输入数据与预期错误文案。使用pytest.mark.parametrize可以避免写三份结构相同的测试pytest.mark.negativepytest.mark.parametrize(first_name, last_name, postal_code, expected_error,[(,Smith,114514,Error: First Name is required),(Jack,,114514,Error: Last Name is required),(Jack,Smith,,Error: Postal Code is required),],ids[missing_first_name,missing_last_name,missing_postal_code,],)deftest_checkout_required_fields(checkout_page,first_name,last_name,postal_code,expected_error):checkout_page.submit_checkout_information(first_name,last_name,postal_code)assertcheckout_page.get_error_message()expected_error参数化后的 pytest 报告会把三组数据分别显示为独立用例失败时可以直接看到是哪一个字段的校验不符合预期。2. 边界测试的价值记录真实行为而不是主观猜测边界测试不只是验证“应该报错”。例如当前站点对普通空格、特殊字符和 256 长度名称的表现并不完全一致普通空格、特殊字符和超长名称可以进入下一步Tab 字符则被判定为未填写。这里的断言记录的是当前产品真实行为而不是替产品预设一条规则。测试人员发现这种差异后可以进一步与产品或开发确认普通空格是否也应该作为空值处理姓名字段是否需要字符集与长度限制图 3 未填写必填项时的错误提示六、使用 Marker 与统一入口执行测试新版本在pytest.ini中登记了三类标签[pytest] markers smoke: 冒烟测试用于验证核心流程 boundary: 边界测试用于验证异常输入边界 negative: 负向测试用于验证错误流程因此测试执行可以按风险和目标灵活选择而不是每次都执行全部用例。命令执行范围适用场景python run_tests.py checkoutCheckout 模块全部用例开发完成后的模块回归python run_tests.py checkout_smokeCheckout 冒烟用例快速验证主流程可用python run_tests.py checkout_negativeCheckout 负向用例校验错误提示与拦截逻辑python run_tests.py cart购物车模块购物车功能变更后的回归python run_tests.py all项目全部测试提测前或完整回归run_tests.py将这些命令集中管理并使用当前 Python 解释器调用 pytest。这样可以减少手输路径、遗漏-m参数或使用错误虚拟环境的问题。run_tests.py的作用与结构run_tests.py不是新的测试用例文件而是项目的测试启动器。它把“要执行什么”从一长串 pytest 命令中抽出来变成更容易记忆的业务命令例如checkout、checkout_smoke和all。文件可以拆成三部分理解PROJECT_ROOTPath(__file__).resolve().parent commands{checkout:[testcases/checkout,-v],checkout_smoke:[testcases/checkout,-m,smoke,-v,],checkout_negative:[testcases/checkout,-m,negative,-v,],all:[testcases,-v],}第一部分PROJECT_ROOT取得项目根目录保证无论从哪个终端位置启动pytest 都在项目目录中运行。第二部分commands是命令映射表键是对作者友好的短命令值是传给 pytest 的真实参数。第三部分负责校验用户输入、拼接命令并透传退出码pytest_command[sys.executable,-m,pytest,*commands[command],]resultsubprocess.run(pytest_command,cwdPROJECT_ROOT,checkFalse,)returnresult.returncode这里选择sys.executable而不是直接执行pytest可以确保使用当前 Python 环境对应的 pytestcwdPROJECT_ROOT让相对路径稳定返回result.returncode则让命令行和后续 CI 能够准确感知测试是否失败。整体执行流程如下python run_tests.py checkout_negative ↓ 读取命令行参数 checkout_negative ↓ 从 commands 中找到测试目录和 Marker 参数 ↓ 组装 python -m pytest testcases/checkout -m negative -v ↓ 在项目根目录启动 pytest并返回真实退出码因此日常运行只需要在项目根目录执行python run_tests.py checkout python run_tests.py checkout_smoke python run_tests.py checkout_negative python run_tests.py all浏览器启动策略也同步做了区分指定单条测试函数时使用可视化浏览器便于断点观察执行文件或模块时使用无头模式适合批量回归同时关闭 Chrome 密码管理和泄露检测相关弹窗减少测试过程中的干扰。七、实际执行结果执行新结算模块python-mpytest testcases/checkout-q本次实际执行结果............ [100%] 12 passed in 108.61s (0:01:48)这 12 条用例覆盖了 Checkout Information 页面当前实现的核心路径、必填拦截和输入边界行为。八、本阶段复盘与下一步计划本阶段的关键变化不只是多了一个页面对象而是让自动化项目开始具备可分模块、可按风险执行、可复用前置业务流程的测试组织能力。本阶段收获页面对象继续遵守“页面行为与断言分离”的原则fixture 将跨页面前置步骤收敛到测试环境中参数化减少了同类必填校验的重复代码Marker 支持将冒烟、负向和边界场景拆分执行通过边界输入记录了当前页面对空格、Tab、特殊字符和超长文本的真实处理方式Checkout 模块 12 条测试用例实际执行通过。下一阶段计划下一篇将继续完成 Checkout Overview 与订单完成页面重点验证购物车商品是否正确带入订单汇总页商品小计、税费、总价的计算是否一致点击 Finish 后是否出现下单成功提示从商品列表页到订单完成页的端到端业务闭环报告、失败截图与日志等工程化能力。如果你也在学习 Selenium、Pytest 和 Page Object Model欢迎一起交流 Web UI 自动化测试中的设计思路与踩坑记录。推荐标签Selenium、Pytest、Python、UI 自动化测试、参数化测试、软件测试