1. 项目概述:为什么你需要一套开箱即用的UI测试脚本?

如果你正在为Web应用的UI自动化测试发愁,脚本编写繁琐、环境配置复杂、报告不够直观,那么今天分享的这套“35个即跑即用的Playwright+Python UI测试脚本”可能就是你的解药。这不仅仅是一堆代码,而是一个经过实战打磨、覆盖了UI测试核心场景的解决方案包。它集成了登录、截图、Allure报告和PO模型等关键模块,旨在让你从“从零搭建框架”的泥潭中解脱出来,直接聚焦于业务测试逻辑本身。

在当前的敏捷开发和DevOps实践中,UI自动化测试是保障前端功能稳定性的重要防线。然而,很多团队卡在了第一步:如何快速搭建一个稳定、可维护且能产出漂亮报告的测试框架?Playwright以其强大的跨浏览器支持、快速的执行速度和可靠的自动等待机制,成为了新时代UI自动化的热门选择。配合Python的简洁语法,可以极大提升脚本开发效率。但光有工具不够,还需要好的“套路”和“模板”。这套脚本的价值,就在于它提供了从元素定位、页面封装、测试用例组织到报告生成的一整套最佳实践范例,你拿到手后,只需替换成自己项目的URL和元素选择器,就能立刻跑起来看到效果,极大降低了自动化测试的入门和集成门槛。

2. 环境准备与核心依赖安装

2.1 Python与Pip环境搭建

一切的基础是一个干净的Python环境。我强烈建议使用Python 3.8或更高版本,因为Playwright对新版本Python的支持和优化更好。如果你还没有安装Python,可以从官网下载安装包,记得在安装时勾选“Add Python to PATH”选项,这样就能在命令行中直接使用 python pip 命令了。

安装完成后,打开终端(Windows上是CMD或PowerShell,Mac/Linux上是Terminal),输入以下命令验证安装是否成功:

python --version
pip --version

如果都能正确显示版本号,说明基础环境OK。接下来,我们需要一个项目目录。我习惯为每个测试项目创建独立的虚拟环境,这样可以避免不同项目间的依赖冲突。使用 venv 模块创建虚拟环境:

# 进入你的工作目录,例如 D:\Projects
cd D:\Projects
# 创建一个名为 playwright-auto-demo 的项目文件夹并进入
mkdir playwright-auto-demo && cd playwright-auto-demo
# 创建虚拟环境,环境文件夹名为 venv
python -m venv venv

创建完成后,激活虚拟环境:

  • Windows (CMD): venv\Scripts\activate.bat
  • Windows (PowerShell): venv\Scripts\Activate.ps1 (可能需要先执行 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser )
  • Mac/Linux: source venv/bin/activate

激活后,命令行提示符前通常会显示 (venv) ,表示你已进入该虚拟环境。

2.2 Playwright与测试框架安装

在激活的虚拟环境中,我们开始安装核心依赖。首先通过pip安装Playwright的Python库:

pip install playwright

这个命令会安装Playwright的核心Python绑定。安装完成后,我们需要安装Playwright所需的浏览器二进制文件(Chromium, Firefox, WebKit)。Playwright很贴心地提供了一条命令来完成:

playwright install

这条命令会下载所有支持的浏览器,虽然会占用一些磁盘空间(约1.5GB),但保证了测试环境的完整性。如果你确定只测试Chrome(或基于Chromium的Edge),可以使用 playwright install chromium 来只安装Chromium,以节省时间和空间。

接下来,安装测试运行框架和报告生成工具。这里我们选择 pytest ,因为它功能强大、插件丰富,是Python生态中最主流的测试框架。同时安装用于生成美观HTML报告的 allure-pytest ,以及用于HTTP请求测试的 pytest-playwright (它提供了对Playwright的pytest fixtures支持):

pip install pytest allure-pytest pytest-playwright

为了确保依赖版本兼容,我通常会固定一组经过验证的版本。你可以创建一个 requirements.txt 文件来管理依赖:

playwright==1.40.0
pytest==7.4.4
allure-pytest==2.13.2
pytest-playwright==0.4.3

然后使用 pip install -r requirements.txt 一次性安装。

注意:安装Playwright浏览器时,可能会因为网络问题导致下载缓慢或失败。如果遇到这种情况,可以尝试设置环境变量 PLAYWRIGHT_DOWNLOAD_HOST 为国内镜像源,例如 https://npmmirror.com/mirrors/playwright/ 。具体方法是在执行 playwright install 前,在命令行中先执行 set PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ (Windows) 或 export PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ (Mac/Linux)。

3. 项目结构设计与PO模型解析

3.1 为什么采用PO模型?

拿到35个脚本,第一眼可能会觉得文件很多。别慌,其核心组织思想是“页面对象模型”。PO模型是一种设计模式,它将每个Web页面抽象成一个类,页面上的元素定位器和操作这些元素的方法都封装在这个类中。这样做的好处非常明显:

  1. 高可维护性 :当页面UI发生变化时,你只需要修改对应的页面对象类中的元素定位器,所有使用该定位器的测试用例都会自动生效,无需四处修改。
  2. 高可读性 :测试用例脚本中不再充斥着一堆复杂的CSS或XPath选择器,而是像“ login_page.enter_username('admin') ”这样贴近自然语言的语句,业务逻辑一目了然。
  3. 低耦合性 :页面对象与测试用例分离,便于团队协作。前端开发改页面,测试人员只需更新对应的PO类。

3.2 标准项目目录结构

一个典型的基于PO模型和pytest的Playwright项目结构如下所示。这35个脚本就是按照这个逻辑组织的,你可以参考这个结构来理解它们:

playwright-auto-demo/
├── requirements.txt          # 项目依赖列表
├── conftest.py              # Pytest全局配置,如Playwright browser fixture定义
├── pytest.ini               # Pytest配置文件,可设置命令行默认参数、标记等
├── pages/                   # 页面对象模型(PO)目录
│   ├── __init__.py
│   ├── base_page.py         # 所有页面对象的基类,封装公共方法(如打开URL、通用等待)
│   ├── login_page.py        # 登录页面对象
│   ├── home_page.py         # 主页页面对象
│   └── ...                  # 其他页面对象
├── tests/                   # 测试用例目录
│   ├── __init__.py
│   ├── test_login.py        # 登录相关测试用例
│   ├── test_screenshot.py   # 截图相关测试用例
│   └── ...                  # 其他测试用例集
├── test_data/               # 测试数据目录(如JSON, YAML, CSV文件)
│   └── users.json
├── reports/                 # 测试报告输出目录(Allure报告生成于此)
│   └── allure-results/      # Allure原始结果数据
├── screenshots/             # 自动截图保存目录
│   └── 20240101_120000_failed_login.png
└── utils/                   # 工具函数目录
    ├── __init__.py
    ├── helpers.py           # 通用帮助函数,如数据生成、文件读取
    └── allure_helpers.py    # 定制Allure报告的辅助函数

在这个结构中, pages 文件夹里的每个 .py 文件代表一个页面, tests 文件夹里的文件代表一组测试用例。 conftest.py 是pytest的“魔法”文件,里面可以定义一些全局的fixture,比如初始化Playwright浏览器,这样所有测试用例都可以直接使用,无需重复编写。

4. 核心脚本模块深度拆解

4.1 登录模块的健壮性实现

登录是大多数Web应用自动化测试的起点,也是最容易出错的环节。一个健壮的登录脚本需要考虑多种场景:正常登录、用户名错误、密码错误、空输入、验证码(如果有)等。在提供的脚本中,你会看到 test_login.py 里包含了这些场景的测试用例。

其核心依赖于 pages/login_page.py 这个页面对象。我们来看看一个典型的登录页面对象是如何封装的:

# pages/login_page.py
from playwright.sync_api import Page
from .base_page import BasePage

class LoginPage(BasePage):
    # 元素定位器,使用字典或类属性管理,清晰易改
    _username_input = "#username"
    _password_input = "#password"
    _login_button = "button[type='submit']"
    _error_message = ".alert-error"

    def __init__(self, page: Page):
        super().__init__(page)
        self.page = page

    def navigate_to_login(self):
        """导航到登录页面"""
        self.page.goto(f"{self.base_url}/login")
        self.wait_for_element(self._username_input) # 使用基类的等待方法

    def enter_credentials(self, username: str, password: str):
        """输入用户名和密码"""
        self.page.fill(self._username_input, username)
        self.page.fill(self._password_input, password)

    def click_login(self):
        """点击登录按钮"""
        self.page.click(self._login_button)

    def get_error_message(self):
        """获取错误提示信息"""
        return self.page.text_content(self._error_message) if self.page.is_visible(self._error_message) else None

    def perform_login(self, username: str, password: str):
        """一站式登录操作:输入并提交"""
        self.enter_credentials(username, password)
        self.click_login()

在测试用例中,使用这个页面对象就非常简洁:

# tests/test_login.py
import pytest
from pages.login_page import LoginPage

def test_successful_login(page, login_page): # page和login_page是conftest.py中定义的fixture
    login_page.navigate_to_login()
    login_page.perform_login("valid_user", "valid_pass")
    # 断言登录成功,例如跳转到首页或出现欢迎语
    assert page.url == "https://example.com/dashboard"
    assert page.is_visible("text=Welcome")

def test_login_with_invalid_password(login_page):
    login_page.navigate_to_login()
    login_page.perform_login("valid_user", "wrong_pass")
    error_msg = login_page.get_error_message()
    assert error_msg is not None
    assert "密码错误" in error_msg

实操心得:对于登录这类关键操作,一定要加入明确的等待。脚本中 wait_for_element 的使用至关重要,它确保页面元素加载完成后再进行操作,避免了因网络延迟或前端渲染导致的“Element not found”错误。此外,将登录操作封装成 perform_login 方法,不仅简化了测试用例,也便于未来如果登录流程增加步骤(如输入验证码)时,只需修改这一处。

4.2 智能截图与失败追踪

UI测试光看通过/失败不够直观,我们需要“看见”发生了什么。Playwright原生支持截图,但如何有效利用是关键。脚本中提供了多种截图策略:

  1. 失败自动截图 :通过pytest的钩子函数,在测试用例失败时自动截取当前页面状态,并保存到指定目录,文件名包含时间戳和用例名,便于追溯。
  2. 关键步骤截图 :在测试用例中主动对重要操作(如提交表单前、获取结果后)进行截图,作为测试证据。
  3. 全屏与元素截图 :不仅可以截取整个页面,还可以针对特定元素(如一个弹窗、一个表格)进行精准截图。

实现失败自动截图通常需要在 conftest.py 中编写一个pytest钩子:

# conftest.py
import pytest
from datetime import datetime
import os

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """
    获取测试用例执行结果,并在失败时截图。
    """
    outcome = yield
    report = outcome.get_result()
    if report.when == "call" and report.failed:
        # 获取测试用例中的page fixture
        page = item.funcargs.get("page")
        if page:
            # 创建截图目录
            screenshot_dir = "screenshots"
            os.makedirs(screenshot_dir, exist_ok=True)
            # 生成带时间戳和用例名的文件名
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            test_name = item.name
            file_name = f"{screenshot_dir}/{timestamp}_{test_name}.png"
            # 执行截图
            page.screenshot(path=file_name, full_page=True) # full_page=True截取完整页面
            # 可以将截图路径附加到Allure报告中(后续章节讲)
            if hasattr(report, 'extra'):
                from allure_commons.types import AttachmentType
                import allure
                allure.attach(page.screenshot(full_page=True), name=f"screenshot_{test_name}", attachment_type=AttachmentType.PNG)

在测试用例中,你也可以主动截图:

def test_checkout_process(page):
    # ... 一些操作
    page.screenshot(path="screenshots/before_payment.png")
    # ... 点击支付
    page.screenshot(path="screenshots/after_payment.png")
    # 或者只截取某个元素
    order_summary = page.locator(".order-summary")
    order_summary.screenshot(path="screenshots/order_details.png")

注意事项:截图会占用磁盘空间,尤其是 full_page=True 截取长图时。建议定期清理旧的截图文件,或者配置只在失败时截图。另外,截图文件名最好包含足够的信息(如用例ID、时间戳),方便在大量文件中快速定位。

4.3 生成专业级Allure测试报告

控制台输出的文字报告不够直观,Allure报告能以清晰的树状结构展示测试套件、用例,并附上步骤、截图、日志,生成非常专业的HTML报告。脚本集成了 allure-pytest ,让你一键生成漂亮报告。

首先,在测试用例和页面对象方法中,使用Allure的装饰器和步骤记录来增强报告可读性:

# tests/test_login.py
import allure
import pytest

@allure.epic("用户认证模块") # 定义特性(大模块)
@allure.feature("登录功能") # 定义功能点
class TestLogin:

    @allure.story("正向用例:使用正确凭据登录") # 定义用户故事
    @allure.title("验证管理员用户登录成功") # 定义测试用例标题
    @allure.severity(allure.severity_level.CRITICAL) # 定义严重级别
    def test_admin_login_success(self, login_page):
        with allure.step("步骤1: 导航到登录页面"):
            login_page.navigate_to_login()
        with allure.step("步骤2: 输入管理员用户名和密码"):
            login_page.enter_credentials("admin", "admin123")
        with allure.step("步骤3: 点击登录按钮"):
            login_page.click_login()
        with allure.step("步骤4: 验证登录成功,跳转到控制台"):
            # 断言...
            assert login_page.page.url.contains("dashboard")

pages/login_page.py 中,也可以为关键操作添加步骤:

import allure

class LoginPage(BasePage):
    @allure.step("在登录页面输入用户名 '{username}' 和密码")
    def enter_credentials(self, username: str, password: str):
        self.page.fill(self._username_input, username)
        self.page.fill(self._password_input, password)
        allure.attach(f"输入的用户名: {username}, 密码长度: {len(password)}", name="登录凭证", attachment_type=allure.attachment_type.TEXT)

运行测试并生成报告需要两步:

  1. 运行测试并收集结果 :使用pytest运行测试,并指定 --alluredir 参数来存放原始的Allure结果数据。
    pytest tests/ --alluredir=./reports/allure-results -v
    
    -v 参数表示详细输出。
  2. 生成HTML报告 :使用Allure命令行工具将上一步收集的结果数据转换成HTML报告。
    allure generate ./reports/allure-results -o ./reports/allure-report --clean
    
    然后打开生成的HTML报告:
    allure open ./reports/allure-report
    
    报告会在你的默认浏览器中打开,你可以看到清晰的测试套件层级、通过率、每个用例的详细步骤、截图和附件。

实操心得:为了将失败截图自动附加到Allure报告中,我们需要增强之前 conftest.py 中的钩子函数。在用例失败时,不仅保存截图到本地,也使用 allure.attach 方法将截图以附件形式添加到报告中。这样在查看Allure报告时,可以直接在失败用例的详情里看到截图,无需再去本地文件夹翻找,极大提升了问题排查效率。

4.4 页面对象模型的进阶封装技巧

基础的PO模型是将页面元素和方法封装在一起。在复杂的项目中,我们可以进一步优化:

  1. 基类封装通用操作 :创建一个 BasePage 类,所有具体的页面对象都继承它。 BasePage 中封装诸如 wait_for_element , click_element , get_text 等几乎所有页面都会用到的方法,以及 base_url 等公共属性。
    # pages/base_page.py
    from playwright.sync_api import Page, expect
    import allure
    
    class BasePage:
        def __init__(self, page: Page):
            self.page = page
            self.base_url = "https://your-app.com" # 可从配置读取
    
        def navigate(self, url_suffix=""):
            full_url = f"{self.base_url}{url_suffix}"
            with allure.step(f"导航到: {full_url}"):
                self.page.goto(full_url)
    
        def wait_for_element(self, selector, state="visible", timeout=30000):
            """等待元素达到特定状态"""
            locator = self.page.locator(selector)
            if state == "visible":
                locator.wait_for(state="visible", timeout=timeout)
            elif state == "hidden":
                locator.wait_for(state="hidden", timeout=timeout)
            return locator
    
        def take_screenshot(self, name):
            """截图并附加到Allure报告"""
            allure.attach(
                self.page.screenshot(full_page=True),
                name=name,
                attachment_type=allure.attachment_type.PNG
            )
    
  2. 组件化封装 :对于网站中重复出现的组件,如导航栏、页脚、模态框,可以单独封装成组件类。页面对象再组合这些组件。
    # pages/components/navbar.py
    class NavBar:
        def __init__(self, page: Page):
            self.page = page
            self._user_menu = "#user-menu"
    
        def logout(self):
            self.page.click(self._user_menu)
            self.page.click("text=退出登录")
    
    # pages/dashboard_page.py
    from .components.navbar import NavBar
    class DashboardPage(BasePage):
        def __init__(self, page: Page):
            super().__init__(page)
            self.navbar = NavBar(page) # 组合导航栏组件
    
  3. 使用数据驱动测试 :将测试数据(如不同的用户名密码组合)从测试脚本中分离出来,存放在JSON、YAML或CSV文件中。使用pytest的 @pytest.mark.parametrize 装饰器来实现数据驱动,使一个测试用例可以覆盖多组数据。
    # test_data/login_data.json
    [
      {"username": "admin", "password": "correct", "expected": "success"},
      {"username": "admin", "password": "wrong", "expected": "fail"},
      {"username": "", "password": "correct", "expected": "fail"}
    ]
    
    # tests/test_login_ddt.py
    import json
    import pytest
    
    with open('./test_data/login_data.json', 'r') as f:
        test_data = json.load(f)
    
    @pytest.mark.parametrize("data", test_data)
    def test_login_with_data(login_page, data):
        login_page.perform_login(data['username'], data['password'])
        if data['expected'] == 'success':
            assert login_page.is_login_successful()
        else:
            assert login_page.get_error_message() is not None
    

5. 脚本的即跑即用与定制化

5.1 如何运行现有脚本?

假设你已经下载或克隆了这35个脚本,并按照第二章配置好了环境。运行它们通常只需几步:

  1. 检查配置 :打开项目根目录下的 conftest.py 或配置文件,查看 base_url 是否指向一个可访问的测试环境。如果是像 https://example.com 这样的公共演示网站,可以直接运行。如果是内部系统,你需要将其修改为你自己测试环境的地址。
  2. 运行全部测试 :在项目根目录下打开终端,激活虚拟环境后,运行最基本的pytest命令。
    pytest
    
    这条命令会自动发现并运行 tests 目录下所有以 test_ 开头的文件。
  3. 运行特定模块或用例
    pytest tests/test_login.py # 运行单个文件
    pytest tests/test_login.py::TestLogin # 运行单个测试类
    pytest tests/test_login.py::TestLogin::test_admin_login_success # 运行单个测试方法
    pytest -k "login" # 运行名称中包含“login”的所有用例
    pytest -m "smoke" # 运行标记为smoke的用例(需要在用例上用@pytest.mark.smoke装饰)
    
  4. 生成并查看报告
    # 运行测试并收集Allure结果
    pytest --alluredir=./reports/allure-results
    # 生成HTML报告
    allure generate ./reports/allure-results -o ./reports/allure-report --clean
    # 打开报告
    allure open ./reports/allure-report
    

5.2 将脚本适配到你自己的项目

“即跑即用”的核心是“模板”。你需要将这些脚本作为模板,修改关键部分以适配你的实际项目:

  1. 修改页面元素定位器 :这是最主要的工作。打开 pages/ 目录下的各个页面对象文件,将里面的CSS选择器或XPath(如 #username , button[type='submit'] )替换成你目标网页上对应元素的真实定位器。使用浏览器的开发者工具(F12)来检查元素并复制选择器。Playwright Recorder(下文会讲)也能极大帮助这件事。
  2. 更新基础URL和导航逻辑 :在 base_page.py 或项目配置中,将 base_url 改为你自己应用的URL。检查各个页面对象的 navigate_to_xxx 方法中的路径是否正确。
  3. 调整测试数据和断言 :在测试用例文件( tests/ 目录下)中,将测试用的用户名、密码、商品ID等数据替换成你测试环境有效的账号。同时,将断言( assert 语句)中的预期结果(如跳转后的URL、页面出现的文本)修改为你的应用实际的行为。
  4. 处理特殊验证 :如果你的登录有验证码、短信验证等环节,脚本中可能没有直接处理。你需要根据情况:
    • 测试环境关闭验证码 :这是最常用的方式,让开发在测试环境提供万能验证码或关闭验证码功能。
    • OCR识别 :对于简单图形验证码,可以考虑集成OCR库(如 pytesseract )进行识别,但稳定性和复杂度较高。
    • 接口绕过 :与开发协商,通过调用后端接口获取或绕过验证码(这需要一定的接口测试知识)。

    注意:自动化测试的原则是测试核心业务流程,像验证码这种专门设计来防止机器人的环节,通常建议在测试环境将其屏蔽或简化处理。

6. 高效编写与维护脚本的实战技巧

6.1 利用Playwright CodeGen快速生成脚本骨架

对于全新的页面,手动编写所有元素操作很耗时。Playwright提供了一个强大的命令行代码生成器,可以录制你的操作并生成Python(或其他语言)脚本。

playwright codegen https://your-test-site.com

执行上述命令会自动打开一个浏览器和一个代码生成窗口。你在浏览器中的所有操作(点击、输入、导航)都会实时转换成代码显示在窗口中。录制完成后,你可以将生成的代码复制到你的页面对象或测试用例中,再进行结构化和封装。这是快速创建初始脚本原型的利器。

6.2 元素定位策略与最佳实践

稳定的元素定位是UI自动化的基石。Playwright支持多种定位方式,优先级建议如下:

  1. Role定位(推荐) :通过元素的ARIA角色、名称进行定位,这是最接近用户感知的方式,可读性最好,且通常不易因前端微调而失效。
    page.get_by_role("button", name="登录").click()
    page.get_by_role("textbox", name="用户名").fill("admin")
    
  2. Text定位 :通过页面上的文本内容定位。
    page.get_by_text("提交订单").click()
    page.get_by_text("欢迎", exact=True).is_visible() # exact=True 表示精确匹配
    
  3. CSS选择器 :最常用和灵活的方式。优先使用 id 、有意义的 class 或属性。
    page.locator("#submit-button").click()
    page.locator(".primary-btn").click()
    page.locator("input[name='email']").fill("test@example.com")
    
  4. XPath(谨慎使用) :功能强大但脆弱,容易因页面结构微小变动而失效。除非没有其他选择,否则尽量避免使用绝对路径或包含索引的XPath。
    # 尽量避免
    page.locator("//div[@id='container']/div[3]/button[2]").click()
    # 相对好一些
    page.locator("//button[contains(text(), '保存')]").click()
    

实操心得:为重要的元素(如核心按钮、表单输入框)在开发阶段就争取加上稳定的 id data-testid 属性,这是对自动化测试最友好的做法。例如 <button data-testid="login-submit-btn">登录</button> ,这样你可以用 page.locator('[data-testid="login-submit-btn"]') 进行非常稳定的定位。

6.3 等待机制:告别“sleep”和“flaky tests”

不稳定的测试(有时成功有时失败)往往是等待处理不当造成的。Playwright内置了自动等待机制,对于大多数操作(如 click , fill )它会等待元素可操作。但有些情况需要显式处理:

  • 等待导航 page.goto(url, wait_until='networkidle') 会等待到网络基本空闲。
  • 等待元素出现/消失 :使用 page.locator(selector).wait_for(state='visible')
  • 等待特定条件 :使用 page.wait_for_function() page.wait_for_url()
  • 等待超时设置 :可以在全局或局部设置更长的超时时间 page.set_default_timeout(60000)

绝对避免使用硬编码的 time.sleep(seconds) ,这是测试脚本性能低下和不稳定的罪魁祸首。

6.4 测试用例的组织与标记

随着脚本增多,需要良好的组织。Pytest提供了丰富的标记功能来对用例进行分类:

import pytest

@pytest.mark.smoke  # 冒烟测试
def test_quick_login():
    pass

@pytest.mark.regression  # 回归测试
@pytest.mark.slow  # 慢速测试
def test_complete_user_journey():
    pass

@pytest.mark.parametrize("browser_name", ["chromium", "firefox"]) # 参数化,跨浏览器测试
def test_cross_browser(browser_name, page): # page fixture会根据browser_name自动创建对应浏览器上下文
    pass

然后可以通过标记来选择性运行:

pytest -m "smoke"  # 只运行冒烟测试
pytest -m "not slow"  # 不运行标记为slow的测试

7. 常见问题排查与性能优化

7.1 典型问题速查表

在实际运行脚本时,你可能会遇到以下常见问题:

问题现象 可能原因 解决方案
Error: Page closed 测试过程中页面被意外关闭或导航。 检查脚本逻辑,确保在操作元素前页面处于稳定状态。使用 page.wait_for_load_state() 确保页面加载完成。
Timeout 30000ms exceeded 元素未在默认30秒内出现或变为可操作状态。 1. 检查元素定位器是否正确,页面是否已加载。
2. 增加超时时间: page.locator(selector).click(timeout=60000)
3. 检查是否有模态框、弹窗遮挡了目标元素。
Element is not visible 元素存在于DOM中,但被隐藏(CSS: display: none )或不可交互。 使用 page.locator(selector).is_visible() 判断。可能需要触发某些操作(如点击下拉箭头)才能使元素显示。
Target closed 浏览器上下文或页面在操作前已被关闭。 检查fixture的生命周期。确保在 @pytest.fixture(scope="function") 的页面对象中,不要在一个用例中关闭了被其他fixture共享的浏览器。
Allure报告为空或只有框架 1. 未正确使用 @allure.step 等装饰器。
2. 运行命令未指定 --alluredir
3. allure-results 目录被清空后才生成报告。
1. 确保用例或页面方法中添加了Allure注解。
2. 运行pytest时务必加上 --alluredir=路径
3. 先 generate open --clean 参数会清空旧结果。
截图是空白或纯色 1. 截图时机不对,页面还未渲染。
2. 在 headless (无头)模式下,某些WebGL或复杂动画可能导致问题。
1. 截图前加入等待: page.wait_for_timeout(1000) 或等待特定元素。
2. 尝试以非无头模式运行:在 conftest.py 的browser fixture中设置 headless=False
脚本在CI/CD中失败,本地却成功 1. CI环境缺少依赖(如浏览器未安装)。
2. CI环境资源(CPU/内存)不足。
3. 网络延迟或测试环境在CI中不可达。
1. 在CI脚本中确保运行了 playwright install
2. 为Playwright配置更长的超时和更少的并行worker。
3. 检查CI环境到测试环境的网络连通性。

7.2 性能优化与稳定运行

  1. 复用浏览器上下文 :在 conftest.py 中,通过设置 browser fixture scope "session" ,可以让所有测试用例共享同一个浏览器实例,而不是每个用例都启动关闭一次,这能极大提升测试速度。
    @pytest.fixture(scope="session")
    def browser():
        with sync_playwright() as p:
            # 可以在这里设置慢速网络、模拟移动设备等
            browser = p.chromium.launch(headless=True) # CI环境通常用headless
            yield browser
            browser.close()
    
    注意,共享浏览器上下文时,每个测试用例应该使用独立的 page scope="function" )来保证测试隔离。
  2. 并行测试 :Pytest支持通过 pytest-xdist 插件进行并行测试。安装后使用 -n 参数指定进程数。
    pip install pytest-xdist
    pytest -n auto # 自动检测CPU核心数并行运行
    
    并行时要注意测试用例之间的独立性,不能有共享状态(如操作同一个全局变量、依赖固定的执行顺序)。
  3. 选择性运行与失败重试
    • 使用 pytest -k 关键字过滤运行相关用例。
    • 使用 pytest --lf --last-failed )只重新运行上次失败的用例。
    • 安装 pytest-rerunfailures 插件,对不稳定的测试进行自动重试。
      pip install pytest-rerunfailures
      pytest --reruns 3 --reruns-delay 2 # 失败后重试3次,每次间隔2秒
      
  4. 资源清理 :确保测试用例在结束时清理测试数据(如删除测试创建的用户、订单),避免测试数据污染影响后续用例。这通常可以通过 @pytest.fixture teardown 操作,或者调用后台清理接口来实现。

7.3 集成到CI/CD流水线

自动化测试只有集成到CI/CD中才能发挥最大价值。以GitHub Actions为例,一个简单的配置可能如下:

# .github/workflows/playwright-test.yml
name: Playwright UI Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.10'
    - name: Install dependencies
      run: |
        pip install -r requirements.txt
        playwright install --with-deps chromium # 只安装Chromium以加快速度
    - name: Run tests
      run: |
        pytest --alluredir=allure-results
      env:
        BASE_URL: ${{ secrets.TEST_ENV_URL }} # 从GitHub Secrets读取测试环境URL
    - name: Upload Allure results
      uses: actions/upload-artifact@v3
      with:
        name: allure-results
        path: allure-results/
    # 可以添加后续步骤:生成并部署Allure报告到静态页面服务

这个工作流会在每次代码推送或PR时自动运行UI测试,并将原始的Allure结果保存为制品,供后续分析或生成报告。

将35个脚本作为种子,结合上述的设计思想、实操技巧和避坑指南,你完全可以根据自己项目的实际情况,生长出一套健壮、高效且易于维护的UI自动化测试体系。记住,自动化测试不是一蹴而就的,而是需要随着项目迭代不断维护和优化的活文档。从核心业务流程开始,逐步覆盖,持续集成,才能真正为你的产品质量保驾护航。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐