第7篇:跨端拓展!Playwright+Appium实现Web+移动端全覆盖

第7篇:跨端拓展!Playwright+Appium实现Web+移动端全覆盖

        前言: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并配置模拟器
  1. 下载Android Studio:https://developer.android.google.cn/studio,按默认步骤安装(Windows需勾选“Android Virtual Device”)
  2. 配置Android SDK: ① 打开Android Studio,进入“Configure”→“SDK Manager” ② 勾选“Android SDK Platform 33”(或最新稳定版本)和“Android SDK Build-Tools 33.0.2” ③ 记住SDK安装路径(如D:\Android\Sdk),后续配置环境变量用
  3. 创建Android模拟器: ① 进入“Configure”→“AVD Manager”→“Create Virtual Device” ② 选择设备类型(如Pixel 6)→ 选择系统镜像(推荐API 33)→ 完成创建 ③ 点击“启动”按钮,验证模拟器是否能正常启动
  4. 配置Android环境变量(Windows): ① 此电脑→属性→高级系统设置→环境变量→系统变量→新建 ② 变量名:ANDROID_HOME,变量值:Android SDK安装路径(如D:\Android\Sdk) ③ 编辑Path变量,添加:%ANDROID_HOME%\platform-tools 和 %ANDROID_HOME%\tools ④ 验证:打开新cmd,输入adb --version,输出版本信息即成功
(2)安装Appium并配置
  1. 下载Appium:https://github.com/appium/appium-desktop/releases,选择最新稳定版本(如1.22.3),按默认步骤安装
  2. 启动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(入口页面),步骤如下:

  1. 启动Android模拟器,安装测试App(如京东App,可从应用商店下载)
  2. 打开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. 运行移动端测试用例

  1. 启动Android模拟器
  2. 启动Appium服务(点击“Start Server”)
  3. 运行测试脚本:python test_ecommerce_app_login.py
  4. 观察模拟器:自动启动京东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),解决偶发失败问题

七、实战练习(巩固跨端框架)

基于整合后的跨端框架,完成以下练习,巩固所学知识:

  1. 新增Web端“百度搜索多组关键词”测试用例(数据驱动,Excel存储关键词)
  2. 新增移动端“京东App商品加入购物车”测试用例(封装购物车页面对象)
  3. 配置Jenkins任务,运行新增的测试用例,生成Allure报告并发送邮件通知
  4. 模拟测试失败场景,验证失败截图和日志是否正常生成

        提示:练习过程中,重点关注“代码复用”和“跨端一致性”,尽量复用框架已有的工具和模块,避免重复开发。遇到问题可查看Allure报告的日志和截图,快速定位问题。

八、总结与下一篇预告

本篇文章我们完成了跨端自动化测试的核心实战,总结重点:

  • Playwright是跨端测试的优选工具,支持Web+移动端+桌面端,内置自动等待、截图录屏等高级功能,稳定性和效率优于Selenium
  • 移动端测试需依赖Appium管理设备,Playwright通过Appium的WebDriver协议控制原生App
  • 跨端框架整合核心:基于PO模式按端划分页面对象,扩展Fixture实现跨端驱动初始化,复用日志、报告、数据驱动模块
  • 企业级落地要点:环境隔离、数据分离、CI/CD集成、稳定性优化

        下一篇文章是系列的最后一篇,我们将进行“综合项目实战+面试冲刺”,通过一个完整的电商平台跨端测试项目巩固全系列知识,同时梳理UI自动化面试高频考点和标准答案,帮你从“学会”到“会用”,轻松应对求职面试!

        如果这篇文章对你有帮助,别忘了点赞+收藏+关注,后续会持续更新UI自动化系列教程~ 有任何问题欢迎在评论区留言!

专栏地址:【保姆级实战】UI自动化从入门到企业落地全系列(持续更新)

Could not load content