基于 Playwright + Pytest + Allure 的 UI 自动化测试框架
- 🚀 开箱即用: 集成 Playwright、Pytest、Allure,无需复杂配置
- 🎯 POM 设计模式: 清晰的页面对象模型,易于维护
- 📊 美观的测试报告: 自动生成 Allure 报告,支持视频录制和截图
- 🔄 状态管理: 智能的浏览器状态管理,避免重复登录
- 🌐 多浏览器支持: 支持 Chromium、Firefox、WebKit、Chrome、Edge
- 📝 详细日志: 基于 loguru 的日志系统,方便调试
- ⚡ 并行测试: 支持多浏览器并行执行
- 🎨 灵活配置: 支持配置继承和覆盖
pip install pwtestgit clone https://github.com/mofanx/pwtest.git
cd pwtest
pip install -e .# 安装框架后,需要安装 Playwright 浏览器
playwright install
# 或者只安装特定浏览器
playwright install chromium# 创建项目目录
mkdir my_test_project
cd my_test_project
# 使用 pwtest 命令初始化项目
pwtest init
# 完成! 所有必需的文件和目录都已创建pwtest init 会自动创建:
- ✅ 目录结构 (config/, pages/, tests/)
- ✅ 配置文件 (pytest.ini, config/config.py)
- ✅ 测试配置 (conftest.py)
- ✅ 示例文件 (login_page.py, test_example.py)
- ✅ 项目文档 (README.md, .gitignore)
如果你想手动创建项目结构:
mkdir my_test_project
cd my_test_project
mkdir -p config pages tests重要: 测试项目需要自己的 pytest.ini,框架内的配置不会自动生效。
# pytest.ini
[pytest]
minversion = 8.0
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --tb=short --capture=no
markers =
smoke: 冒烟测试用例
regression: 回归测试用例
user: 需要全新浏览器实例的测试# config/config.py
from pwtest import Config as BaseConfig
class Config(BaseConfig):
BASE_URL = "https://your-test-site.com"
VALID_USERNAME = "user"
VALID_PASSWORD = "password"
# 可选: 覆盖框架默认配置
HEADLESS = False
VIDEO_RECORD = True
settings = Config()# conftest.py
from pwtest.conftest import * # 导入框架的所有 fixtures
from config.config import settings
# 定义 login_callback 以启用登录状态管理
@pytest.fixture(scope="session")
def login_callback():
"""提供登录回调函数
此 fixture 会被框架自动检测和使用。
如果不需要登录状态管理,可以不定义此 fixture。
"""
def do_login(page):
# 方式 1: 使用页面对象(推荐)
from pages.login_page import LoginPage
login_page = LoginPage(page)
login_page.login(settings.VALID_USERNAME, settings.VALID_PASSWORD)
# 方式 2: 直接操作(简单场景)
# page.goto("/login")
# page.fill("#username", settings.VALID_USERNAME)
# page.fill("#password", settings.VALID_PASSWORD)
# page.click("button[type='submit']")
return do_login注意:
login_callback是可选的,只有需要登录状态管理时才定义- 框架会自动检测并使用此 fixture
- 如果不定义,每个测试需要自己处理登录
# pages/login_page.py
class LoginPage:
def __init__(self, page):
self.page = page
self.username_input = page.get_by_placeholder("请输入账号")
self.password_input = page.get_by_placeholder("请输入密码")
self.login_button = page.get_by_role("button", name="登录")
def login(self, username: str, password: str):
self.page.goto("/login")
self.username_input.fill(username)
self.password_input.fill(password)
self.login_button.click()# tests/test_example.py
import allure
@allure.feature("登录功能")
@allure.story("用户登录")
def test_successful_login(page):
"""测试成功登录"""
page.goto("/dashboard")
expect(page).to_have_title("预期标题")# 运行所有测试
pytest tests/ -v
# 运行指定浏览器
pytest tests/ --browsers=chromium
# 运行多个浏览器
pytest tests/ --browsers=chromium,firefox
# 生成 Allure 报告
pytest tests/ --alluredir=allure-results
allure serve allure-results框架提供了丰富的配置选项,可以通过继承 Config 类来覆盖默认配置:
from pwtest import Config as BaseConfig
class Config(BaseConfig):
# 基础配置
BASE_URL = "https://example.com"
# 浏览器配置
BROWSER_TYPE = "chromium" # chromium, firefox, webkit
HEADLESS = False
SLOW_MO = 100 # 操作间隔(毫秒)
# 超时配置
DEFAULT_TIMEOUT = 30000
NAVIGATION_TIMEOUT = 30000
# 视窗配置
VIEWPORT_WIDTH = 1920
VIEWPORT_HEIGHT = 1080
# 截图配置
SCREENSHOT_ON_FAILURE = True
SCREENSHOT_PATH = "screenshots"
# 视频录制配置
VIDEO_RECORD = True
VIDEO_PATH = "videos"
AUTO_CLEANUP_PASSED_VIDEOS = True
# 操作录制配置
TRACING_ON = False
TRACING_PATH = "tracings"
AUTO_CLEANUP_PASSED_TRACINGS = True
# 业务配置
VALID_USERNAME = "testuser"
VALID_PASSWORD = "password123"
STATE_PATH = "state.json"
settings = Config()框架使用 login_callback 机制实现登录逻辑的解耦:
@pytest.fixture(scope="session")
def login_callback():
"""提供登录回调函数"""
def do_login(page):
# 方式1: 使用页面对象
login_page = LoginPage(page)
login_page.login(username, password)
# 方式2: 直接操作
# page.goto("/login")
# page.fill("#username", username)
# page.fill("#password", password)
# page.click("button[type='submit']")
return do_login# 单个浏览器
pytest --browsers=chromium
# 多个浏览器
pytest --browsers=chromium,firefox,webkit
# 所有浏览器
pytest --browsers=all
# 使用系统浏览器
pytest --browsers=chrome # 或 msedge框架预定义了常用的测试标记:
@pytest.mark.smoke
def test_critical_feature(page):
"""冒烟测试"""
pass
@pytest.mark.no_login
def test_login(page):
"""需要全新浏览器的测试"""
pass
@pytest.mark.regression
def test_all_features(page):
"""回归测试"""
pass# pages/base_page.py
class BasePage:
def __init__(self, page):
self.page = page
def navigate_to(self, path):
self.page.goto(path)
# pages/login_page.py
class LoginPage(BasePage):
def login(self, username, password):
# 登录逻辑
passimport allure
from pages.login_page import LoginPage
@allure.feature("用户管理")
@allure.story("用户登录")
@allure.severity(allure.severity_level.CRITICAL)
def test_login(page):
login_page = LoginPage(page)
with allure.step("访问登录页"):
login_page.goto_login()
with allure.step("输入用户名和密码"):
login_page.input_username(settings.VALID_USERNAME)
login_page.input_password(settings.VALID_PASSWORD)
with allure.step("点击登录按钮"):
login_page.click_login_button()import pytest
@pytest.mark.parametrize("username,password,expected", [
("user1", "pass1", True),
("user2", "pass2", False),
])
def test_login_with_data(page, username, password, expected):
# 测试逻辑
passimport os
class Config:
BASE_URL = os.getenv("TEST_BASE_URL", "https://default.com")
USERNAME = os.getenv("TEST_USERNAME", "default_user")
PASSWORD = os.getenv("TEST_PASSWORD", "default_pass")# conftest.py
@pytest.fixture
def api_client():
"""API客户端fixture"""
return APIClient(base_url=settings.API_URL)
@pytest.fixture
def test_data():
"""测试数据fixture"""
return load_test_data("test_data.json")# 覆盖context fixture
@pytest.fixture(scope="session")
def context(browser):
"""自定义context配置"""
context = browser.new_context(
viewport={"width": 1280, "height": 720},
# 自定义配置
)
yield context
context.close()框架自动生成详细的测试报告:
- Allure 报告: 美观的 HTML 报告,包含测试步骤、截图、视频
- HTML 报告: pytest-html 生成的简单报告
- 日志文件: 详细的测试执行日志
临时查看 Allure 报告:
allure serve allure-results生成 Allure 报告:
allure generate ./allure-results -o ./allure-report --clean --lang zh
allure open ./allure-report欢迎提交 Issue 和 Pull Request!
初始化测试项目,自动创建所有必需的文件和目录。
# 在当前目录初始化
pwtest init
# 在指定目录初始化
pwtest init --dir my_project
# 强制覆盖已存在的文件
pwtest init --force显示框架版本信息。
pwtest --version# 安装构建工具
pip install build twine
# 构建包
python -m build
# 检查包
twine check dist/*# 发布到 TestPyPI (测试)
twine upload --repository testpypi dist/*
# 发布到 PyPI (正式)
twine upload dist/*- GitHub: https://github.com/mofanx/pwtest
- 作者: mofanx
- Issues: https://github.com/mofanx/pwtest/issues
感谢以下开源项目:
- Playwright 文档 - 强大的浏览器自动化工具
- Pytest 文档 - 优秀的 Python 测试框架
- Allure 文档 - 美观的测试报告框架
pwtest - 让 UI 自动化测试更简单! 🚀