TPshop商城UI自动化实战:Pytest+PO模式+Allure报告全流程
发布时间:2026/9/26 7:48:02 作者:尧图编辑部 阅读量:1,286

做UI自动化测试这几年我越来越认可一个观点与其自己造轮子做练习不如拿现成的真实项目来练手。TPshop这个商城项目就是我经常推荐给团队新人的一个高质量实战靶场。它包含前台用户端、后台管理端页面元素丰富流程复杂几乎覆盖了Web UI自动化中80%的典型场景。而Allure测试报告则是让测试结果从“干巴巴的Pass/Fail”变成“一眼看懂问题在哪”的关键工具。这篇内容我就把从TPshop项目部署、PytestPO模式框架搭建到集成Allure生成漂亮报告的完整过程以及我踩过的那些坑一次性讲清楚。我尽量把每一步都写得可以直接照着做尤其是一些配置文件、代码结构和关键参数会给出完整的示例。如果你已经把Selenium的基础语法玩熟了正想找个项目做综合性实战或者被一堆“绿油油”的JUnit风格报告逼疯了想换成Allure这种信息密度高的报告这篇内容值得你花几分钟看完。1. TPshop项目实战项目背景与部署思路1.1 为什么选择TPshop商城作为UI自动化练习项目网上商城类练习项目很多比如那些几十行代码拼凑的“购物车Demo”或者是需要庞大PHP环境的OpenCart。但TPshop这个项目我实测下来最适合做UI自动化原因有三点。首先是页面元素足够真实。TPshop的前台模仿了主流电商的布局头部搜索、商品分类导航、轮播图、商品列表、详情页、购物车、结算页、订单确认每一步都有丰富的表单和按钮交互不像Demo项目那样只有几个静态链接。自动化脚本需要处理下拉选择、单选按钮、弹窗提示、异步加载这些高频业务动作TPshop都能提供。其次是环境部署相对简单。这个项目基于PHPMySQL有现成的一键安装包也有源码部署方式。对于测试同学来说不需要深入了解后端开发细节按照文档把数据库导进去、改一下配置文件就能跑起来。整个过程大概三十分钟比那些需要配置Java Maven多模块工程的项目友好太多。第三是它自带了完整的前后台数据流。从后台添加商品、修改价格再到前台购买整个订单状态流转是可闭环的这一点对需要断言数据回写的自动化脚本来说特别重要。很多练习项目只管前端展示后台根本不维护数据导致脚本执行完“看起来成功”了但实际业务逻辑根本没跑通。1.2 本地部署TPshop的完整步骤与避坑点如果你还没有部署过TPshop我这里给出一个相对省心的操作路径。建议在Windows笔记本上用phpStudy或者同类集成环境搭建实在想用Docker也行但本地部署调试起来更直观。第一步下载TPshop源码。从官方渠道获取最新的独立版商城源码压缩包解压后放到phpStudy的www目录下。注意目录名最好不要带中文和空格否则后面配置环境时容易出现各种诡异问题。第二步启动Apache和MySQL服务。在phpStudy面板上把两个服务状态调成绿色。这里有个我踩过的坑MySQL端口冲突。如果你电脑上装了其他数据库占用了3306端口需要提前改掉或者把TPshop配置里的端口改成实际可用的。第三步创建数据库并导入数据。用phpMyAdmin创建一个名为tpshop的数据库字符集选utf8然后把源码包里的tpshop.sql一般在application/admin/controller/同级的data目录里导入进去。如果在导入时遇到“脚本超时”或者“内存不足”的提示多半是index.sql文件太大解决办法是调整php.ini里的max_execution_time和memory_limit或者用数据库客户端工具分段导入。第四步修改应用配置文件。配置文件在application/database.php里把hostname、database、username、password这几项改成你本机实际的值。注意TPshop默认的数据库前缀可能是tpshop_别改错了。第五步登录后台完成初始化。前台和后台地址分别是http://localhost/tpshop/index.php和http://localhost/tpshop/index.php/Admin。首次访问后台会要求设置管理员账号设置完就能正常管理商品和订单了。这个过程里最常见的问题就是安装完成后前台能打开但后台登录后一片空白。我遇到过几次原因基本都是PHP版本过高导致的类库兼容问题解决办法是把phpStudy里的PHP版本切换到5.6或7.0TPshop的代码比较老对PHP7.4以上的支持不是很好。2. 自动化测试框架搭建PytestPO模式做基础2.1 工具选型为什么是PytestSelenium而不是其他组合现在UI自动化工具圈里Playwright、Cypress都很热门甚至还有基于LangChain的智能生成脚本方向。但我在TPshop项目上依然选择PytestSelenium原因不是排斥新技术而是考虑到Pytest生态在断言、夹具、报告集成方面太成熟了特别是配合Allure时它的特性支持几乎是完美的。Pytest的优势在于测试用例的收集和执行规则简单assert原生的断言失败信息友好而且有pytest.fixture这种处理前后置的机制正好对应UI自动化里“打开浏览器”“登录系统”“清理数据”这些重复操作。再看Selenium它对浏览器的控制能力依然最稳定尤其是处理老项目或者特定浏览器版本时兼容性远好于新兴框架。当然如果你对Playwright感兴趣后面我可以专门写一篇如何在TPshop上迁移脚本。但就目前基于Pytest的生态成熟度来说它还是最适合结合Allure做企业级可视化报告的方案。我始终觉得工具只是手段能稳定持续产出有价值报告的框架才是王道。2.2 项目目录结构与公共模块封装我的TPshop自动化项目目录结构如下这个结构经过了多轮优化能有效避免用例越多越混乱的问题tpshop_ui_test/ ├── config/ │ ├── settings.ini │ └── elements.ini ├── page/ │ ├── base_page.py │ ├── home_page.py │ ├── login_page.py │ └── product_page.py ├── testcases/ │ ├── conftest.py │ ├── test_login.py │ └── test_flow_purchase.py ├── data/ │ └── test_cases.yaml ├── report/ │ └── allure-results/ ├── utils/ │ ├── driver_factory.py │ └── logger_util.py └── requirements.txt我会把最关键的两个公共模块文件贴出来说明因为这决定了后续用例的编写效率。首先是utils/driver_factory.py这个模块负责创建和回收浏览器驱动。给它加了一个读取配置文件的基础能力让它支持通过环境变量或者配置文件切换测试环境地址、浏览器类型以及是否走无头模式。# utils/driver_factory.py import functools import os from selenium import webdriver from selenium.webdriver.chrome.options import Options functools.lru_cache(maxsize1) def get_driver(browserchrome, headlessFalse): if browser.lower() chrome: options Options() if headless: options.add_argument(--headlessnew) options.add_argument(--start-maximized) options.add_argument(--ignore-certificate-errors) options.add_experimental_option(excludeSwitches, [enable-logging]) driver webdriver.Chrome(optionsoptions) else: options webdriver.FirefoxOptions() if headless: options.add_argument(-headless) driver webdriver.Firefox(optionsoptions) driver.implicitly_wait(5) return driver然后是page/base_page.pyBasePage把Selenium的WebDriver操作封装成了更贴近业务的语言。这里面最有价值的是我加了一个“智能等待并高亮元素”的方法凡是关键操作必须等元素可见才去执行这样能显著减少因为页面加载慢导致的断言失败。# page/base_page.py import time from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from utils.logger_util import logger class BasePage: def __init__(self, driver): self.driver driver def find_element(self, locator, timeout10): try: element WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) # 高亮显示方便在Allure截图中定位 self.driver.execute_script( arguments[0].setAttribute(style, arguments[1]), element, border: 2px solid red;, ) return element except Exception: logger.error(f定位元素失败: {locator}) self.take_screenshot(locator_error) raise def click(self, locator): element self.find_element(locator) element.click() def input_text(self, locator, text): element self.find_element(locator) element.clear() element.send_keys(text) def take_screenshot(self, name): ts time.strftime(%Y%m%d_%H%M%S) path freport/screenshots/{name}_{ts}.png self.driver.save_screenshot(path) return path这里的核心思路是“把页面上的每一个交互动作都沉淀成方法”。比如登录页的login_page.py不会直接在测试用例里写driver.find_element(By.ID, username).send_keys(admin)而是暴露一个login(username, password)方法。这样做的好处有三个第一当页面元素发生变动时只需要修改Page类里的定位器不用去动测试用例第二测试用例读起来像业务步骤团队协作时每个人都能看懂第三在Allure报告里我们可以把方法的每一步都生成一个步骤节点定位问题时会精确到具体操作。2.3 Conftest与Fixture搞定浏览器生命周期和登录状态Pytest的conftest.py是我在这个项目里的核心资产。它把浏览器启动、登录取session、关闭浏览器这些重复操作都变成了自动执行的固件。我最常用的是一个login_fixture用于那些必须先登录才能执行的交易类用例。看这个关键文件# testcases/conftest.py import pytest from utils.driver_factory import get_driver from page.login_page import LoginPage pytest.fixture(scopefunction) def browser(): driver get_driver() yield driver driver.quit() pytest.fixture(scopeclass) def logged_in_browser(browser): login_page LoginPage(browser) login_page.open() login_page.login(test_user, 123456) yield browser # 不需要额外操作browser fixture 会负责退出关于scope的选择我建议不要一上来就全部用module或者session级别的复用。虽然那样执行速度会快很多但如果一个测试类里的用例有相互影响比如第一个用例把商品加入购物车第二个用例却想清空空购物车复用同一个浏览器会带来状态污染。稳妥起见交易流程类用例用function级别先保障隔离性登录、注册这类独立用例可以适当扩大复用范围。2.4 测试用例编写实战从登录流到完整购物结算我一般会把用例分成两层业务功能测试和数据驱动测试。业务功能用例走真实流程数据驱动用例用来覆盖大量输入组合。比如登录用例用同一个方法跑多组数据# testcases/test_login.py import pytest from page.login_page import LoginPage from utils.logger_util import logger class TestLogin: pytest.mark.parametrize( username,password,expected, [ (test_user, 123456, 用户中心), (test_user, wrong_pwd, 用户名或密码错误), (, 123456, 请输入用户名), ], ) def test_login_valid_and_invalid(self, browser, username, password, expected): login_page LoginPage(browser) login_page.open() login_page.login(username, password) actual_text login_page.get_login_result_text() assert expected in actual_text完整购物流程用例我倾向于把它拆成多个小函数每一步都加入截图保存这样即使执行到最后一步报错也能知道是在加购、下单还是支付环节出的问题# testcases/test_flow_purchase.py import allure from page.home_page import HomePage from page.product_page import ProductDetailPage from page.cart_page import CartPage from page.checkout_page import CheckoutPage allure.feature(购物流程) class TestPurchaseFlow: allure.story(登录用户正常购买一件商品) def test_full_purchase(self, logged_in_browser): home_page HomePage(logged_in_browser) product_page ProductDetailPage(logged_in_browser) cart_page CartPage(logged_in_browser) checkout_page CheckoutPage(logged_in_browser) with allure.step(打开首页并搜索商品): home_page.search_product(手机) with allure.step(进入商品详情页并加入购物车): product_page.add_to_cart() with allure.step(进入购物车并去结算): cart_page.go_to_settlement() with allure.step(填写收货信息并提交订单): checkout_page.submit_order() with allure.step(校验订单提交成功): assert 订单提交成功 in checkout_page.get_success_message()注意到我在代码里用了allure.step。这样在最终报告里每个操作步骤都会以折叠块的形式呈现执行到哪一步挂了点开就能看到具体操作名称和关联截图排查效率比看普通日志高得多。3. Allure测试报告从安装到产出高质量可视化报告3.1 为什么Allure报告值得“折腾”很多测试项目还在用pytest自带的控制台输出或者HTML插件生成的简单表格。那些报告只回答了“哪些用例通过了、哪些挂了”但完全没有回答“每个用例到底做了哪些步骤”“失败时页面状态长什么样”这些问题。Allure报告的价值在三个方面体现得非常明显第一视觉效果专业按功能模块、严重程度、接口分组领导和技术团队都能快速定位重点第二信息链路完整每个用例的执行时间、步骤日志、断言信息、截图、日志文件都能挂载在测试步骤上第三历史趋势记录——Allure会自动保留每次执行的历史记录多次迭代后能直观看到稳定性变化。这一个功能就让报告从“测试结果”转变成了“质量分析资产”。3.2 Allure的安装与环境变量配置Allure本身是一个独立的命令行工具依赖Java环境。所以安装步骤分为两步。第一步安装Java JDK。至少需要1.8以上的版本。如果本地已经有其他Java程序建议用java -version先确认版本兼容。如果电脑上还没有Java推荐用Adoptium的安装包装完自己会配好环境变量。第二步安装Allure命令行工具。Windows下最省事的方法是使用scoop install allure或者去官方GitHub Releases页面下载zip包然后解压到指定目录把bin目录路径加到系统环境变量PATH里。Mac下则可以用brew install allure。验证是否安装成功执行allure --version会输出类似2.27.0的版本号。这里有个容易踩的坑很多同学在安装后直接跑allure serve report结果提示“不是内部或外部命令”。根本原因是环境变量只对新打开的命令行窗口生效。如果你用了PyCharm的Terminal必须完全重启PyCharm才能读到新的环境变量。3.3 Pytest集成Allure的实战配置集成工作分为两部分一是安装pytest-allure-adapter插件二是在运行命令时指定结果输出目录。安装插件pip install pytest allure-pytest然后在项目根目录创建pytest.ini配置文件可以很灵活地指定测试目录、结果目录和默认参数[pytest] addopts -vs --alluredir report/allure-results --clean-alluredir testpaths testcases markers smoke: 冒烟测试 full: 全量回归这里的--alluredir report/allure-results是告诉pytest把Allure要使用的JSON结果文件写到这个目录下。--clean-alluredir是每次运行前清理旧结果避免和历史数据混在一起如果希望保留历史趋势就不要加这个参数。执行测试用例后目录下会生成一堆result-xxx.json和container-xxx.json文件。这些是Allure的原始数据需要使用Allure命令行生成HTML报告。生成报告有两种方式方式一直接本地起服务查看推荐调试时用allure serve report/allure-results这种方式会启动一个临时HTTP服务自动在默认浏览器打开报告不用自己手动生成静态文件而且所有历史记录和分类都自动合并。方式二生成静态HTML文件适合发邮件存档或挂到CI流水线allure generate report/allure-results -o report/allure-report --clean allure open report/allure-report在Jenkins或者Github Actions里通常会执行allure generate然后把report/allure-report目录上传为构建产物。3.4 让报告信息量翻倍的装饰器与步骤技巧我见过很多团队虽然集成了Allure但报告里全是“TestLogin::test_case_001”看起来索然无味。原因就是没有合理使用Allure的装饰器。下面这些是我在项目里实际用到的关键装饰器每一个都有明确的场景import allure allure.epic(TPshop商城UI自动化) allure.feature(登录模块) allure.story(用户登录) allure.title(使用合法账号登录预期跳转到用户中心) allure.severity(allure.severity_level.BLOCKER) allure.link(http://backlog.uri/to/123) def test_login_success(): ...allure.epic最高层级的业务分组一般对应整个项目或大版本。allure.feature对应功能模块比如登录、商品、购物车。这个层级决定报告首页“Features”看板上的结构。allure.story对应具体业务场景比如“用户登录”“忘记密码”。allure.title给用例一个人类可读的标题这一步极其重要。默认的用例标题是函数名中文环境下很容易让人看不懂。allure.severity标注用例的严重级别配合报告首页的“Severity”图表可以快速筛选阻塞性问题。allure.step在用例内部把具体操作封装成步骤我记得它和allure.step的上下文管理器是配套的。用这种写法报告中的每个步骤都能展开查看其内部日志。另外我在BasePage里特意封装了截图挂载功能只要调用allure.attach.file或者allure.attach方法就可以把截图嵌入报告。截图文件太大也不是问题可以用Base64压缩或者直接以PNG原始文件附加。下面是类似实现# utils/allure_helper.py import allure import pytest def attach_screenshot(driver, namepage_screenshot): allure.attach( driver.get_screenshot_as_png(), namename, attachment_typeallure.attachment_type.PNG, )在用例断言失败时自动截图并附加到报告比手动在except里埋点可靠得多。推荐在conftest.py的pytest_runtest_makereport钩子里实现失败时自动抓图。4. 实战中反复踩过的坑环境问题、元素定位与Allure坑4.1 ChromeDriver版本匹配问题UI自动化最经典的老大难问题就是浏览器驱动和浏览器版本不匹配。Chrome一升级ChromeDriver立刻罢工。我遇到最多的是SessionNotCreatedException提示This version of ChromeDriver only supports Chrome version xxx。这个问题彻底防住的办法是在启动驱动前加一层检测逻辑。我写了一个简单的工具方法先从Chrome安装目录读取版本号前两位再动态从镜像地址拉取对应的Driver二进制。但考虑到不同网络环境差异更稳妥的方式是使用webdriver-manager这个库它会自动处理版本匹配pip install webdriver-manager代码里这样写from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager driver webdriver.Chrome(ChromeDriverManager().install())用这个方案后我再没手工下载过ChromeDriver。如果你所在的网络环境拉取不到驱动那就老老实实手动到下载页面找到和当前Chrome大版本号一致的文件。4.2 元素定位失败与隐式等待的冲突TPshop首页采用了不少异步加载尤其搜索框下拉联想和购物车按钮状态。很多新人写脚本时喜欢把所有find_element前都加上sleep(3)这种“盲等”不仅慢而且不稳定。我调试时习惯把BasePage里的“等待并高亮”功能录制下来。比如点击购物车之前先用WebDriverWait等待购物车图标出现再等待购物车内的商品数量变为“1”。这种等待条件比单纯人畜无害的“元素可见”更贴近真实业务。如果定位还是不稳定优先考虑要不要换一个定位策略。有好几次我以为元素加载慢实际上是页面上有两个一样的class名而我的find_element取到了隐藏的那个。改用driver.find_elements并且根据可见性筛选才最终锁定目标。4.3 Allure报告生成时常见异常Allure本身在执行过程中报错的可能不多但生成报告阶段容易出问题。第一个坑执行完pytest后allure serve提示“Could not find any test result files”。最常见的原因是--alluredir指定的目录和serve时的目录不一致。有的同学在pytest命令里写的是report/allure-results但生成时用了allure serve report/allure-report当然找不到原始数据。必须让serve或generate指向保存原始JSON文件的目录。第二个坑报告页面打开后是0个用例或者用例数不对。这通常是pytest没有正常收集到用例可能是testcases目录下文件名字不符合test_*.py规则或者是pytest.ini里的testpaths配置错了。先单独跑一下pytest --collect-only确认用例数再排查报告问题。第三个坑历史趋势不显示或者显示空白。Allure报告的历史记录是从生成目录里读取之前执行的history文件并复制过来的。如果用allure generate生成新报告时没有带上一份旧报告就没有历史。想要趋势图最好每次执行完成后把上次那次生成的history文件夹拷贝到本次allure-results下或者直接用allure serve因为serve每次都会基于现有results生成一份包含历史的报告。4.4 跨浏览器执行与并发稳定性做UI自动化不能只盯Chrome尤其是商城类项目用户很可能用Firefox或Edge。我的做法是把浏览器配置做成一个命令行参数运行pytest时通过--browser firefox传入。在Fixture里读取会话级别的配置根据配置创建对应驱动实例。并发方面Pytest本身是串行的如果用例量特别大可以考虑引入pytest-xdist。但在TPshop项目上我不建议一上来就玩并发因为商城业务前后端耦合紧密共享一个后台库存数据时多进程操作会互相干扰。真要想提速我会优先区分哪些用例是独立的比如登录页校验、搜索页展示把这些用例划为独立分组再单独并行跑。最后分享两个我自己项目里的实用小技巧技巧一Allure报告里可以挂载日志。我习惯在pytest.ini或者conftest里配置好logging的格式然后在每个Page类的关键操作里用logging.info输出参数和操作结果。这样Allure报告里的“Steps”下点开每个步骤能看到详细的日志输出排查问题非常方便。技巧二给用例加上“自动重试”功能也有讲究。UI自动化里很多失败是环境抖动造成的比如网络慢导致的元素加载超时。我写了一个简单的flaky装饰器在关键用例上最多重试两次但只在第一次失败原因明确是超时才重试如果是断言错误就立即停止。这样既不影响失败分析又能大幅提升整体成功率。TPshop项目配合PytestPO模式Allure是我认为目前Web UI自动化中最适合“从入门到进阶”的组合。如果照着这篇内容跑通了整个流程建议你下一步把用例补充到20条以上再试着集成到你所在团队的测试流水线里。到时候你会发现自动化测试最让人头疼的“可读性”和“可维护性”早就在框架设计阶段解决了。