【教程】用Python+Playwright打造自动化测试框架:从零搭建到CI/CD集成
一、前置条件
在开始之前,请确保你的环境满足以下条件:
- Python 3.8+ 已安装
- pip 包管理器可用
- 一个代码编辑器(推荐 VS Code)
- Git 已安装(用于版本管理)
二、为什么选Playwright?
最近uniTerm v1.0正式开源发布,深度集成AI Agent的终端智能能力引发热议。而在Web自动化测试领域,Playwright正以碾压之势取代Selenium成为新标杆。
核心优势:
- 自动等待:告别time.sleep(),元素操作自动等待就绪
- 多浏览器:Chromium、Firefox、WebKit一键切换
- 录制生成:codegen命令自动生成测试代码
- Trace Viewer:可视化回溯测试执行过程
- 并行执行:原生支持多worker并发测试
三、步骤1:环境搭建
- # 创建项目目录
- mkdir playwright-demo && cd playwright-demo
- # 创建虚拟环境
- python -m venv venv
- source venv/bin/activate # Linux/Mac
- # venv\Scripts\activate # Windows
- # 安装Playwright
- pip install pytest-playwright
- # 安装浏览器二进制文件
- playwright install
复制代码
验证安装:- playwright --version
- # 输出类似:Version 1.49.0
复制代码
四、步骤2:编写第一个测试
创建 test_example.py:
- import re
- from playwright.sync_api import Page, expect
- def test_has_title(page: Page):
- page.goto("https://playwright.dev/")
-
- # 断言页面标题包含Playwright
- expect(page).to_have_title(re.compile("Playwright"))
- def test_get_started_link(page: Page):
- page.goto("https://playwright.dev/")
-
- # 点击Get started链接
- page.get_by_role("link", name="Get started").click()
-
- # 断言页面URL包含intro
- expect(page).to_have_url(re.compile(".*intro"))
复制代码
运行测试:- pytest test_example.py --headed # 有界面模式
- pytest test_example.py # 无头模式(推荐CI使用)
复制代码
五、步骤3:Page Object模式实战
对于复杂项目,推荐Page Object模式组织代码。以登录功能为例:
创建 pages/login_page.py:
- from playwright.sync_api import Page
- class LoginPage:
- def __init__(self, page: Page):
- self.page = page
- self.username_input = page.locator("#username")
- self.password_input = page.locator("#password")
- self.login_button = page.locator("button[type='submit']")
- self.error_message = page.locator(".alert-danger")
-
- def navigate(self, url: str):
- self.page.goto(url)
- return self
-
- def login(self, username: str, password: str):
- self.username_input.fill(username)
- self.password_input.fill(password)
- self.login_button.click()
- return self
-
- def get_error_text(self) -> str:
- return self.error_message.inner_text()
复制代码
创建 test_login.py:
- import pytest
- from playwright.sync_api import Page
- from pages.login_page import LoginPage
- class TestLogin:
- def test_successful_login(self, page: Page):
- login_page = LoginPage(page)
- login_page.navigate("https://example.com/login")
- login_page.login("admin", "correct_password")
-
- # 断言登录成功后的跳转
- assert page.url == "https://example.com/dashboard"
-
- def test_failed_login(self, page: Page):
- login_page = LoginPage(page)
- login_page.navigate("https://example.com/login")
- login_page.login("admin", "wrong_password")
-
- # 断言错误提示
- error_text = login_page.get_error_text()
- assert "用户名或密码错误" in error_text
复制代码
六、步骤4:数据驱动与参数化
使用pytest参数化实现数据驱动:
- import pytest
- from playwright.sync_api import Page
- from pages.login_page import LoginPage
- @pytest.mark.parametrize("username,password,expected", [
- ("admin", "correct_pass", "success"),
- ("admin", "wrong_pass", "error"),
- ("", "", "error"),
- ("invalid_user", "any_pass", "error"),
- ])
- def test_login_scenarios(page: Page, username, password, expected):
- login_page = LoginPage(page)
- login_page.navigate("https://example.com/login")
- login_page.login(username, password)
-
- if expected == "success":
- assert "dashboard" in page.url
- else:
- error_text = login_page.get_error_text()
- assert len(error_text) > 0
复制代码
七、步骤5:CI/CD集成(GitHub Actions)
创建 .github/workflows/playwright.yml:
- name: Playwright Tests
- on:
- push:
- branches: [main, develop]
- pull_request:
- branches: [main]
- jobs:
- test:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: '3.11'
-
- - name: Install dependencies
- run: |
- pip install pytest-playwright
- playwright install --with-deps
-
- - name: Run tests
- run: pytest --tracing=retain-on-failure
-
- - name: Upload trace
- if: failure()
- uses: actions/upload-artifact@v4
- with:
- name: playwright-traces
- path: test-results/
复制代码
八、步骤6:高级技巧
1. 自动录制视频与截图
- # pytest.ini
- [pytest]
- addopts = --video=retain-on-failure --screenshot=only-on-failure
复制代码
2. 移动端模拟
- from playwright.sync_api import sync_playwright
- with sync_playwright() as p:
- iphone = p.devices['iPhone 14']
- browser = p.chromium.launch()
- context = browser.new_context(**iphone)
- page = context.new_page()
- page.goto('https://example.com')
复制代码
3. API拦截与Mock
- # 拦截API请求并返回Mock数据
- page.route("**/api/users", lambda route: route.fulfill(
- status=200,
- content_type="application/json",
- body='{"users": [{"id": 1, "name": "Mock User"}]}'
- ))
复制代码
九、常见问题
Q1: 测试在CI中失败,本地正常?
A: 通常是等待时间问题。Playwright的自动等待已很智能,但复杂异步加载场景建议增加显式等待:- page.wait_for_load_state('networkidle')
复制代码
Q2: 如何处理动态变化的元素定位?
A: 优先使用语义化定位(get_by_role/get_by_text),避免依赖CSS选择器:- page.get_by_role("button", name="提交").click()
复制代码
Q3: 测试执行太慢?
A: 启用并行执行:- pytest -n auto # 需要安装pytest-xdist
复制代码
Q4: 如何调试失败的测试?
A: 使用Trace Viewer:- pytest --tracing=on
- playwright show-trace trace.zip
复制代码
十、总结
通过本教程,我们完成了:
- Playwright环境搭建与基础测试编写
- Page Object模式实现代码复用
- 参数化实现数据驱动测试
- GitHub Actions CI/CD流水线集成
- 视频录制、移动端模拟、API Mock等高级技巧
Playwright的生态系统正在快速成熟,配合pytest的强大功能,可以构建企业级的自动化测试体系。建议下一步学习:
- 视觉回归测试(playwright-snapshot)
- 测试报告集成(Allure/Playwright HTML Report)
- 分布式测试执行(Selenium Grid替代方案)
参考资料:
Playwright Python官方文档
Playwright Python GitHub
本教程基于Playwright v1.49+编写,如有更新请以官方文档为准。 |