前言:Hello 大家好!我是励志死磕计算机~ 前面我们已经完成了Web UI自动化的企业级落地(Jenkins+Allure),但在真实企业场景中,测试需求往往不局限于Web端------移动端App、桌面端应用的测试需求同样高频。传统方案中,Web用Selenium、移动端用Appium,需要维护两套技术栈,学习成本和维护成本都很高。今天这篇文章,我们就学习当前最火的跨端自动化工具Playwright,实现"Web+移动端App"的统一测试,一套技术栈搞定多端需求,大幅提升测试效率!全程代码实战,复制即可运行,新手也能轻松上手~
本文核心目标:
-
理解Playwright核心优势,掌握其与Selenium的差异(为什么企业越来越倾向Playwright)
-
完成Playwright全环境搭建(Web+移动端),解决环境配置高频问题
-
实战Playwright Web端核心操作,感受自动等待、内置截图等优势
-
实现Playwright+Appium联动,完成移动端App(Android)自动化测试
-
整合Playwright+Pytest+Allure,搭建跨端测试框架,复用企业级落地能力
一、前置知识:为什么选择Playwright?跨端测试核心价值
在动手操作前,先明确Playwright的核心优势和跨端测试的企业价值,避免盲目学习:
1. Playwright核心优势(对比Selenium)
| 对比维度 | Selenium | Playwright |
|---|---|---|
| 跨端支持 | 仅Web端(Chrome/Firefox/Edge等) | Web端、移动端(Android/iOS App)、桌面端应用(Windows/macOS/Linux) |
| 等待机制 | 需手动配置显式/隐式等待,易出现不稳定问题 | 内置自动等待(元素可交互时才执行操作),稳定性大幅提升 |
| 驱动管理 | 需手动下载或用webdriver-manager管理,易出现版本不匹配 | 自动下载浏览器二进制文件和驱动,无需手动管理 |
| 核心功能 | 基础交互操作,高级功能需依赖第三方插件 | 内置截图/录屏、网络拦截、模拟设备、无痕模式等高级功能 |
| 学习成本 | 较低,但企业落地需额外封装大量工具 | 略高,但内置企业级功能,落地效率更高 |
2. 跨端测试的企业价值
-
降低学习成本:一套技术栈搞定多端测试,无需同时掌握Selenium、Appium等工具
-
降低维护成本:跨端框架可复用代码(如日志、报告、数据驱动模块),无需维护多套框架
-
提升测试效率:统一的测试流程和报告,便于团队协作和项目管理
-
契合业务需求:当前互联网产品多为"Web+App"联动,跨端测试可覆盖完整用户场景
3. 本文实战技术栈
Playwright(跨端自动化核心)+ Python(编程语言)+ Pytest(测试框架)+ Appium(移动端联动)+ Allure(测试报告)+ Android Studio(Android模拟器)
二、前置准备:全环境搭建(Web+移动端)
环境搭建是跨端测试的基础,分为"Playwright基础环境""Web端环境""移动端环境"三部分,全程图文步骤,避开配置坑:
1. Playwright基础环境搭建
Playwright的环境搭建非常简单,核心是安装Playwright库并自动下载浏览器二进制文件:
(1)安装Playwright库
打开cmd/终端,执行以下命令(建议在虚拟环境中安装,避免依赖冲突):
# 安装Playwright(指定稳定版本)
pip install playwright==1.40.0
# 自动下载浏览器二进制文件(Chrome、Firefox、Edge)和驱动
playwright install
小技巧:如果下载缓慢,可配置国内镜像源: pip install playwright==1.40.0 -i https://pypi.tuna.tsinghua.edu.cn/simple playwright install 命令也可指定镜像:playwright install --proxy-server https://pypi.tuna.tsinghua.edu.cn/simple
(2)验证Playwright安装
创建test_playwright_install.py文件,执行以下代码,若能成功启动Chrome并访问百度,则基础环境搭建成功:
from playwright.sync_api import sync_playwright
# 启动Playwright
with sync_playwright() as p:
# 启动Chrome浏览器(headless=False表示显示浏览器界面)
browser = p.chromium.launch(headless=False)
# 创建新页面
page = browser.new_page()
# 访问百度
page.goto("https://www.baidu.com")
# 打印页面标题
print("页面标题:", page.title())
# 关闭浏览器
browser.close()
运行命令:python test_playwright_install.py,输出"页面标题:百度一下,你就知道"即成功。
2. 移动端环境搭建(Android为例)
移动端测试需要依赖Android Studio(模拟器)和Appium(设备管理),步骤如下:
(1)安装Android Studio并配置模拟器
-
下载Android Studio:https://developer.android.google.cn/studio,按默认步骤安装(Windows需勾选"Android Virtual Device")
-
配置Android SDK: ① 打开Android Studio,进入"Configure"→"SDK Manager" ② 勾选"Android SDK Platform 33"(或最新稳定版本)和"Android SDK Build-Tools 33.0.2" ③ 记住SDK安装路径(如D:\Android\Sdk),后续配置环境变量用
-
创建Android模拟器: ① 进入"Configure"→"AVD Manager"→"Create Virtual Device" ② 选择设备类型(如Pixel 6)→ 选择系统镜像(推荐API 33)→ 完成创建 ③ 点击"启动"按钮,验证模拟器是否能正常启动
-
配置Android环境变量(Windows): ① 此电脑→属性→高级系统设置→环境变量→系统变量→新建 ② 变量名:ANDROID_HOME,变量值:Android SDK安装路径(如D:\Android\Sdk) ③ 编辑Path变量,添加:%ANDROID_HOME%\platform-tools 和 %ANDROID_HOME%\tools ④ 验证:打开新cmd,输入
adb --version,输出版本信息即成功
(2)安装Appium并配置
-
下载Appium:https://github.com/appium/appium-desktop/releases,选择最新稳定版本(如1.22.3),按默认步骤安装
-
启动Appium:打开Appium,默认端口4723,点击"Start Server"启动服务(显示"Appium REST http interface listener started on 0.0.0.0:4723"即成功)
(3)安装移动端相关依赖
# Appium Python客户端(Playwright联动Appium需要)
pip install appium-python-client==2.10.0
# 用于解析AndroidManifest.xml(获取App包名和Activity)
pip install xmltodict==0.13.0
3. 项目依赖更新
在之前的企业级框架根目录,更新requirements.txt文件,添加Playwright及移动端相关依赖:
pytest==7.4.0
pytest-html==3.2.0
selenium==4.11.2
webdriver-manager==4.0.0
python-dotenv==1.0.0
openpyxl==3.1.2
pytest-rerunfailures==12.0
pytest-timeout==2.1.0
allure-pytest==2.13.5
playwright==1.40.0
appium-python-client==2.10.0
xmltodict==0.13.0
将更新后的requirements.txt推送到Git仓库,便于后续Jenkins集成。
三、核心实战一:Playwright Web端自动化测试
Playwright Web端操作与Selenium类似,但API更简洁,且内置自动等待机制,稳定性更高。我们以"百度搜索+淘宝商品搜索"为实战场景,覆盖核心操作:
1. Playwright核心API快速上手
先掌握Playwright最常用的核心API,后续实战会反复用到:
from playwright.sync_api import sync_playwright, expect
# 1. 启动Playwright(上下文管理器模式,自动关闭资源)
with sync_playwright() as p:
# 2. 启动浏览器(chromium/firefox/webkit,headless=False显示界面)
browser = p.chromium.launch(headless=False, slow_mo=500) # slow_mo=500:慢动作执行,便于观察
# 3. 创建上下文(可设置cookie、本地存储等)
context = browser.new_context()
# 4. 创建新页面
page = context.new_page()
# 5. 核心操作示例
# 访问页面
page.goto("https://www.baidu.com")
# 输入文本(定位方式:id选择器,自动等待元素可交互)
page.locator("#kw").fill("Playwright 教程")
# 点击元素(定位方式:id选择器)
page.locator("#su").click()
# 6. 断言(Playwright内置expect,更强大)
# 断言页面标题包含"Playwright 教程"
expect(page).to_have_title("Playwright 教程_百度搜索")
# 断言搜索结果中包含"Playwright 官方文档"
expect(page.locator('text="Playwright 官方文档"')).to_be_visible()
# 7. 内置截图(全屏截图)
page.screenshot(path="baidu_search_result.png", full_page=True)
# 8. 录屏(需在创建context时开启)
# context = browser.new_context(record_video_dir="videos/") # 开启录屏,保存到videos目录
# page.video.save_as("baidu_search_video.mp4") # 保存录屏
# 9. 关闭资源(上下文管理器自动关闭,也可手动关闭)
page.close()
context.close()
browser.close()
2. 实战:淘宝商品搜索全流程
实现"访问淘宝→输入商品关键词→点击搜索→筛选销量排序→断言结果"全流程,覆盖Playwright高级操作(如iframe切换、悬浮操作):
from playwright.sync_api import sync_playwright, expect
import time
def test_taobao_product_search():
with sync_playwright() as p:
# 启动Chrome,慢动作500ms
browser = p.chromium.launch(headless=False, slow_mo=500)
context = browser.new_context()
page = context.new_page()
# 1. 访问淘宝首页
page.goto("https://www.taobao.com")
# 等待页面加载完成(Playwright自动等待,也可手动设置等待条件)
page.wait_for_load_state("networkidle") # 等待网络空闲(无网络请求)
# 2. 处理淘宝首页的iframe(搜索框在iframe中)
# 切换到iframe(通过name定位)
page.frame_locator("iframe[name='s_iframe']").locator("#q").fill("Python编程书籍")
# 3. 点击搜索按钮
page.frame_locator("iframe[name='s_iframe']").locator("button[type='submit']").click()
# 4. 筛选销量排序(悬浮操作)
# 定位销量排序元素并悬浮
sales_sort = page.locator("a[href*='sort=sale-desc']")
page.hover(sales_sort)
time.sleep(1) # 短暂等待,便于观察
sales_sort.click()
# 5. 等待搜索结果加载完成
page.wait_for_load_state("networkidle")
# 6. 断言:搜索结果中包含"Python编程书籍"
expect(page.locator("div[class='item J_MouserOnverReq ']")).to_have_count_greater_than(10)
expect(page.locator("text='Python编程书籍'")).to_be_visible()
# 7. 截图保存结果
page.screenshot(path="taobao_search_result.png", full_page=True)
# 关闭资源
page.close()
context.close()
browser.close()
if __name__ == "__main__":
test_taobao_product_search()
运行命令:python test_taobao_search.py,可看到浏览器自动完成整个搜索流程,截图保存在当前目录。
3. Playwright Web端核心优势总结
-
自动等待:无需手动配置显式等待,元素可交互时才执行操作,大幅降低不稳定问题
-
简洁的定位方式:支持CSS、XPath、文本、id等多种定位方式,API更直观(如locator("#kw"))
-
内置高级功能:截图、录屏、iframe切换、网络拦截等功能开箱即用,无需额外封装
-
上下文隔离:每个context独立,可实现多用户并发测试,不相互干扰
四、核心实战二:Playwright+Appium实现移动端App测试
Playwright本身支持移动端WebView测试,若需测试原生App,需与Appium联动。我们以"电商App登录+商品浏览"为实战场景,完成移动端自动化测试:
1. 前置准备:获取App包名和启动Activity
测试原生App前,需获取App的包名(package)和启动Activity(入口页面),步骤如下:
-
启动Android模拟器,安装测试App(如京东App,可从应用商店下载)
-
打开cmd,执行以下命令,启动App并获取相关信息:
启动App(以京东App为例,包名和Activity需自行获取)
adb shell am start -n com.jingdong.app.mall/.main.MainActivity
获取当前运行App的包名和Activity
adb shell dumpsys window | findstr mCurrentFocus
输出结果中,"com.jingdong.app.mall"是包名,"/.main.MainActivity"是启动Activity。
2. Playwright+Appium联动配置
通过Appium管理Android设备,Playwright通过Appium的WebDriver协议控制App,核心配置如下:
from playwright.sync_api import sync_playwright
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
import time
def test_ecommerce_app_login():
# 1. 配置Appium选项
options = UiAutomator2Options()
# 设备名称(在Android Studio的AVD Manager中查看)
options.set_capability("deviceName", "Pixel 6 API 33")
# 平台名称
options.set_capability("platformName", "Android")
# 平台版本(模拟器的Android版本)
options.set_capability("platformVersion", "13")
# App包名
options.set_capability("appPackage", "com.jingdong.app.mall")
# 启动Activity
options.set_capability("appActivity", ".main.MainActivity")
# 不重置App状态(避免每次启动都重新安装)
options.set_capability("noReset", True)
# 2. 连接Appium服务
appium_driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", options=options)
appium_driver.implicitly_wait(10)
try:
# 3. 模拟用户操作(登录京东App)
# 点击"我的"tab(通过xpath定位)
my_tab = appium_driver.find_element(AppiumBy.XPATH, "//*[@text='我的']")
my_tab.click()
time.sleep(2)
# 点击"登录/注册"按钮
login_btn = appium_driver.find_element(AppiumBy.XPATH, "//*[@text='登录/注册']")
login_btn.click()
time.sleep(2)
# 输入手机号(通过id定位)
phone_input = appium_driver.find_element(AppiumBy.ID, "com.jingdong.app.mall:id/et_phone")
phone_input.send_keys("13800138000") # 测试手机号
time.sleep(1)
# 点击"获取验证码"按钮
verify_code_btn = appium_driver.find_element(AppiumBy.XPATH, "//*[@text='获取验证码']")
verify_code_btn.click()
time.sleep(2)
# 输入验证码(测试环境,假设验证码为123456)
code_input = appium_driver.find_element(AppiumBy.ID, "com.jingdong.app.mall:id/et_code")
code_input.send_keys("123456")
time.sleep(1)
# 点击"登录"按钮
confirm_login_btn = appium_driver.find_element(AppiumBy.XPATH, "//*[@text='登录']")
confirm_login_btn.click()
time.sleep(3)
# 4. 断言登录成功(通过"我的订单"元素是否可见判断)
my_order = appium_driver.find_element(AppiumBy.XPATH, "//*[@text='我的订单']")
assert my_order.is_displayed(), "登录失败,未找到'我的订单'元素"
print("登录成功!")
# 5. 截图保存结果
appium_driver.save_screenshot("app_login_success.png")
# 6. 商品浏览(点击"首页"tab→搜索商品)
home_tab = appium_driver.find_element(AppiumBy.XPATH, "//*[@text='首页']")
home_tab.click()
time.sleep(2)
# 点击搜索框
search_input = appium_driver.find_element(AppiumBy.ID, "com.jingdong.app.mall:id/search_text")
search_input.click()
time.sleep(1)
# 输入商品关键词
search_input.send_keys("Python编程书籍")
time.sleep(1)
# 点击搜索按钮
search_btn = appium_driver.find_element(AppiumBy.XPATH, "//*[@text='搜索']")
search_btn.click()
time.sleep(3)
# 断言搜索结果
product_item = appium_driver.find_element(AppiumBy.XPATH, "//*[@class='android.widget.TextView' and contains(@text,'Python编程书籍')]")
assert product_item.is_displayed(), "商品搜索失败"
print("商品搜索成功!")
except Exception as e:
print(f"测试失败:{str(e)}")
appium_driver.save_screenshot("app_test_failed.png")
raise
finally:
# 7. 关闭Appium连接
time.sleep(2)
appium_driver.quit()
if __name__ == "__main__":
test_ecommerce_app_login()
4. 运行移动端测试用例
-
启动Android模拟器
-
启动Appium服务(点击"Start Server")
-
运行测试脚本:
python test_ecommerce_app_login.py -
观察模拟器:自动启动京东App,完成登录、商品搜索流程,截图保存在当前目录
常见问题:若出现"设备未找到"错误,检查:① 模拟器是否启动;② adb是否能识别设备(执行adb devices查看);③ Appium配置的deviceName、platformVersion是否与模拟器一致。
五、核心实战三:跨端测试框架整合(Playwright+Pytest+Allure)
前面我们分别实现了Web端和移动端的测试,现在将两者整合到之前的企业级框架中,实现"一套框架、多端测试、统一报告"的目标。框架目录结构如下(基于第4篇的PO模式框架扩展):
ui_auto_framework/ # 项目根目录
├── config/ # 配置层
│ └── config.py # 全局配置(Web地址、App包名、Activity等)
├── pages/ # 页面对象层(按端划分)
│ ├── web/ # Web端页面对象
│ │ ├── base_page.py
│ │ ├── baidu_page.py
│ │ └── taobao_page.py
│ └── app/ # 移动端页面对象
│ ├── base_page.py
│ └── jd_page.py
├── tests/ # 测试用例层(按端划分)
│ ├── web/
│ │ └── test_web_search.py
│ └── app/
│ └── test_app_login.py
├── utils/ # 工具层(复用之前的工具)
│ ├── excel_utils.py
│ ├── json_utils.py
│ ├── logger.py
│ ├── screenshot_utils.py
│ └── appium_utils.py # 新增:Appium工具封装
├── testdata/ # 测试数据层
├── logs/ # 日志输出目录
├── screenshots/ # 截图输出目录
├── allure-results/ # Allure报告原始数据
├── conftest.py # Pytest Fixture配置(扩展跨端驱动初始化)
├── pytest.ini # Pytest配置
└── requirements.txt # 依赖清单
1. 封装跨端驱动Fixture(conftest.py)
扩展conftest.py,添加Web端和移动端的驱动初始化Fixture,支持按标记筛选测试用例:
# 文件名:conftest.py
import pytest
from playwright.sync_api import sync_playwright
from appium import webdriver
from appium.options.android import UiAutomator2Options
from config.config import config
from utils.logger import logger
from utils.screenshot_utils import take_screenshot
# ---------------------- Web端驱动Fixture ----------------------
@pytest.fixture(scope="function")
def web_driver():
logger.info("开始初始化Web端驱动(Playwright)")
with sync_playwright() as p:
# 启动Chrome浏览器
browser = p.chromium.launch(headless=config.HEADLESS, slow_mo=config.SLOW_MO)
context = browser.new_context()
page = context.new_page()
logger.info("Web端驱动初始化完成")
yield page
# 后置操作:关闭驱动
logger.info("开始关闭Web端驱动")
page.close()
context.close()
browser.close()
logger.info("Web端驱动已关闭")
# ---------------------- 移动端驱动Fixture ----------------------
@pytest.fixture(scope="function")
def app_driver():
logger.info("开始初始化移动端驱动(Appium+Playwright)")
# 配置Appium选项
options = UiAutomator2Options()
options.set_capability("deviceName", config.DEVICE_NAME)
options.set_capability("platformName", config.PLATFORM_NAME)
options.set_capability("platformVersion", config.PLATFORM_VERSION)
options.set_capability("appPackage", config.APP_PACKAGE)
options.set_capability("appActivity", config.APP_ACTIVITY)
options.set_capability("noReset", True)
# 连接Appium服务
driver = webdriver.Remote(config.APPIUM_SERVER_URL, options=options)
driver.implicitly_wait(config.IMPLICITLY_WAIT)
logger.info("移动端驱动初始化完成")
yield driver
# 后置操作:关闭驱动
logger.info("开始关闭移动端驱动")
driver.quit()
logger.info("移动端驱动已关闭")
# ---------------------- 失败截图Fixture ----------------------
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()
setattr(item, "rep_call", rep)
# 为Web端用例添加失败截图
@pytest.fixture(scope="function", autouse=True)
def web_failure_screenshot(request, web_driver):
yield
if request.node.rep_call.failed:
take_screenshot(web_driver, request.node.name)
# 为移动端用例添加失败截图
@pytest.fixture(scope="function", autouse=True)
def app_failure_screenshot(request, app_driver):
yield
if request.node.rep_call.failed:
take_screenshot(app_driver, request.node.name)
# ---------------------- 标记筛选 ----------------------
# 为Web端用例添加标记
def pytest_configure(config):
config.addinivalue_line("markers", "web: 运行Web端测试用例")
config.addinivalue_line("markers", "app: 运行移动端测试用例")
2. 封装页面对象(PO模式)
按端划分页面对象,封装重复操作,提升代码复用率:
(1)Web端页面对象(pages/web/taobao_page.py)
# 文件名:pages/web/taobao_page.py
from playwright.sync_api import expect
from pages.web.base_page import BaseWebPage
class TaobaoPage(BaseWebPage):
def __init__(self, page):
super().__init__(page)
self.search_iframe = page.frame_locator("iframe[name='s_iframe']")
self.search_input = self.search_iframe.locator("#q")
self.search_btn = self.search_iframe.locator("button[type='submit']")
self.sales_sort_btn = page.locator("a[href*='sort=sale-desc']")
self.product_item = page.locator("div[class='item J_MouserOnverReq ']")
# 访问淘宝首页
def goto_taobao(self):
self.page.goto("https://www.taobao.com")
self.page.wait_for_load_state("networkidle")
# 搜索商品
def search_product(self, product_name):
self.search_input.fill(product_name)
self.search_btn.click()
self.page.wait_for_load_state("networkidle")
# 筛选销量排序
def sort_by_sales(self):
self.page.hover(self.sales_sort_btn)
self.sales_sort_btn.click()
self.page.wait_for_load_state("networkidle")
# 断言搜索结果
def assert_search_result(self, product_name, min_count=10):
expect(self.product_item).to_have_count_greater_than(min_count)
expect(self.page.locator(f"text='{product_name}'")).to_be_visible()
(2)移动端页面对象(pages/app/jd_page.py)
# 文件名:pages/app/jd_page.py
from appium.webdriver.common.appiumby import AppiumBy
from pages.app.base_page import BaseAppPage
class JdPage(BaseAppPage):
def __init__(self, driver):
super().__init__(driver)
self.my_tab = (AppiumBy.XPATH, "//*[@text='我的']")
self.login_btn = (AppiumBy.XPATH, "//*[@text='登录/注册']")
self.phone_input = (AppiumBy.ID, "com.jingdong.app.mall:id/et_phone")
self.verify_code_btn = (AppiumBy.XPATH, "//*[@text='获取验证码']")
self.code_input = (AppiumBy.ID, "com.jingdong.app.mall:id/et_code")
self.confirm_login_btn = (AppiumBy.XPATH, "//*[@text='登录']")
self.my_order = (AppiumBy.XPATH, "//*[@text='我的订单']")
self.home_tab = (AppiumBy.XPATH, "//*[@text='首页']")
self.search_input = (AppiumBy.ID, "com.jingdong.app.mall:id/search_text")
self.search_confirm_btn = (AppiumBy.XPATH, "//*[@text='搜索']")
self.product_item = (AppiumBy.XPATH, "//*[@class='android.widget.TextView' and contains(@text,'{product_name}')]")
# 点击我的tab
def click_my_tab(self):
self.find_element(self.my_tab).click()
self.driver.implicitly_wait(2)
# 点击登录按钮
def click_login_btn(self):
self.find_element(self.login_btn).click()
self.driver.implicitly_wait(2)
# 输入手机号
def input_phone(self, phone):
self.find_element(self.phone_input).send_keys(phone)
self.driver.implicitly_wait(1)
# 点击获取验证码
def click_get_verify_code(self):
self.find_element(self.verify_code_btn).click()
self.driver.implicitly_wait(2)
# 输入验证码
def input_verify_code(self, code):
self.find_element(self.code_input).send_keys(code)
self.driver.implicitly_wait(1)
# 确认登录
def confirm_login(self):
self.find_element(self.confirm_login_btn).click()
self.driver.implicitly_wait(3)
# 断言登录成功
def assert_login_success(self):
assert self.find_element(self.my_order).is_displayed(), "登录失败"
# 点击首页tab
def click_home_tab(self):
self.find_element(self.home_tab).click()
self.driver.implicitly_wait(2)
# 搜索商品
def search_product(self, product_name):
self.find_element(self.search_input).click()
self.driver.implicitly_wait(1)
self.find_element(self.search_input).send_keys(product_name)
self.driver.implicitly_wait(1)
self.find_element(self.search_confirm_btn).click()
self.driver.implicitly_wait(3)
# 断言商品搜索成功
def assert_product_search_success(self, product_name):
locator = (AppiumBy.XPATH, f"//*[@class='android.widget.TextView' and contains(@text,'{product_name}')]")
assert self.find_element(locator).is_displayed(), "商品搜索失败"
3. 编写跨端测试用例
(1)Web端测试用例(tests/web/test_web_taobao_search.py)
# 文件名:tests/web/test_web_taobao_search.py
import pytest
from pages.web.taobao_page import TaobaoPage
from utils.logger import logger
@pytest.mark.web # 标记为Web端用例
class TestWebTaobaoSearch:
def test_taobao_product_search(self, web_driver):
logger.info("开始执行Web端淘宝商品搜索用例")
taobao_page = TaobaoPage(web_driver)
# 执行测试步骤
taobao_page.goto_taobao()
taobao_page.search_product("Python编程书籍")
taobao_page.sort_by_sales()
# 断言
taobao_page.assert_search_result("Python编程书籍")
logger.info("Web端淘宝商品搜索用例执行成功")
(2)移动端测试用例(tests/app/test_app_jd_login.py)
# 文件名:tests/app/test_app_jd_login.py
import pytest
from pages.app.jd_page import JdPage
from utils.logger import logger
@pytest.mark.app # 标记为移动端用例
class TestAppJdLogin:
def test_jd_app_login_and_search(self, app_driver):
logger.info("开始执行移动端京东App登录+搜索用例")
jd_page = JdPage(app_driver)
# 执行测试步骤
jd_page.click_my_tab()
jd_page.click_login_btn()
jd_page.input_phone("13800138000")
jd_page.click_get_verify_code()
jd_page.input_verify_code("123456")
jd_page.confirm_login()
# 断言登录成功
jd_page.assert_login_success()
# 商品搜索
jd_page.click_home_tab()
jd_page.search_product("Python编程书籍")
# 断言搜索成功
jd_page.assert_product_search_success("Python编程书籍")
logger.info("移动端京东App登录+搜索用例执行成功")
4. 运行跨端测试用例并生成Allure报告
使用Pytest命令运行指定端的测试用例,生成Allure报告:
# 运行Web端用例,生成Allure报告
pytest tests/web/ -v -s -m web --alluredir=./allure-results --clean-alluredir
# 运行移动端用例,生成Allure报告
pytest tests/app/ -v -s -m app --alluredir=./allure-results --clean-alluredir
# 运行所有用例,生成Allure报告
pytest tests/ -v -s --alluredir=./allure-results --clean-alluredir
# 查看Allure报告
allure serve ./allure-results
运行完成后,打开Allure报告,可看到Web端和移动端用例的执行结果、截图、日志等信息,实现统一报告展示。
六、企业级跨端测试落地要点
将跨端测试框架落地到企业时,需注意以下要点,提升框架的实用性和稳定性:
1. 环境隔离与配置管理
-
使用配置文件(config.py)管理不同环境的配置(如测试环境、生产环境的Web地址、App包名)
-
通过环境变量区分测试端(如export TEST_ENV=web 或 export TEST_ENV=app),实现一键切换测试端
2. 测试数据分离
-
将Web端和移动端的测试数据(如账号密码、商品关键词)存储在Excel/JSON文件中,通过数据驱动实现多组测试
-
复用之前封装的excel_utils.py和json_utils.py工具,无需重复开发
3. CI/CD集成(Jenkins)
-
在Jenkins中配置两个任务:一个运行Web端用例,一个运行移动端用例(或一个任务通过参数选择测试端)
-
移动端测试需在Jenkins服务器配置Android模拟器或连接真实设备(推荐使用云设备,如Testin、阿里云测)
4. 稳定性优化
-
Web端:利用Playwright的自动等待机制,减少显式等待的使用
-
移动端:处理手势操作(如滑动、缩放)时,使用Appium的TouchAction类,提升操作稳定性
-
添加失败重试机制(pytest-rerunfailures),解决偶发失败问题
七、实战练习(巩固跨端框架)
基于整合后的跨端框架,完成以下练习,巩固所学知识:
-
新增Web端"百度搜索多组关键词"测试用例(数据驱动,Excel存储关键词)
-
新增移动端"京东App商品加入购物车"测试用例(封装购物车页面对象)
-
配置Jenkins任务,运行新增的测试用例,生成Allure报告并发送邮件通知
-
模拟测试失败场景,验证失败截图和日志是否正常生成
提示:练习过程中,重点关注"代码复用"和"跨端一致性",尽量复用框架已有的工具和模块,避免重复开发。遇到问题可查看Allure报告的日志和截图,快速定位问题。
八、总结与下一篇预告
本篇文章我们完成了跨端自动化测试的核心实战,总结重点:
-
Playwright是跨端测试的优选工具,支持Web+移动端+桌面端,内置自动等待、截图录屏等高级功能,稳定性和效率优于Selenium
-
移动端测试需依赖Appium管理设备,Playwright通过Appium的WebDriver协议控制原生App
-
跨端框架整合核心:基于PO模式按端划分页面对象,扩展Fixture实现跨端驱动初始化,复用日志、报告、数据驱动模块
-
企业级落地要点:环境隔离、数据分离、CI/CD集成、稳定性优化
下一篇文章是系列的最后一篇,我们将进行"综合项目实战+面试冲刺",通过一个完整的电商平台跨端测试项目巩固全系列知识,同时梳理UI自动化面试高频考点和标准答案,帮你从"学会"到"会用",轻松应对求职面试!
如果这篇文章对你有帮助,别忘了点赞+收藏+关注,后续会持续更新UI自动化系列教程~ 有任何问题欢迎在评论区留言!