第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自动化从入门到企业落地全系列(持续更新)

相关推荐
AC梦29 分钟前
unity中如何将UI上的字高清显示
ui·unity
Java面试题总结1 小时前
基于 Java 的 PDF 文本水印实现方案(iText7 示例)
java·python·pdf
不懒不懒1 小时前
【决策树算法实战指南:从原理到Python实现】
python·决策树·id3·c4.5·catr
马猴烧酒.1 小时前
【面试八股|Java集合】Java集合常考面试题详解
java·开发语言·python·面试·八股
天空属于哈夫克31 小时前
Java 版:利用外部群 API 实现自动“技术开课”倒计时提醒
数据库·python·mysql
zhengfei6111 小时前
自动化快速评估工具
运维·自动化
喵手1 小时前
Python爬虫实战:全站 Sitemap 自动发现 - 解析 sitemap.xml → 自动生成抓取队列的工业级实现!
爬虫·python·爬虫实战·零基础python爬虫教学·sitemap·解析sitemap.xml·自动生成抓取队列实现
luoluoal1 小时前
基于深度学习的web端多格式纠错系统(源码+文档)
python·mysql·django·毕业设计·源码
深蓝海拓2 小时前
PySide6从0开始学习的笔记(二十七) 日志管理
笔记·python·学习·pyqt
m_136872 小时前
n8n 启动时报 EACCES permission denied 的完整排查与修复
自动化·n8n