Python + Playwright + Pytest UI自动化

项目结构

project/
├── pages/
│├── __init__.py
│├── base_page.py
│└── login_page.py
├── tests/
│├── __init__.py
│├── conftest.py
│└── test_login.py
├── pytest.ini
└── requirements.txt

1. requirements.txt

pytest==7.4.3
playwright==1.40.0
pytest-playwright==0.4.3

2. base_page.py

from playwright.sync_api import Page, expect

class BasePage:
def __init__(self, page: Page):
self.page = page

def navigate(self, url: str):
self.page.goto(url)

def get_title(self) -> str:
return self.page.title()

def get_url(self) -> str:
return self.page.url

3. login_page.py

from playwright.sync_api import Page, expect
from pages.base_page import BasePage

class LoginPage(BasePage):
# Locators
USERNAME_INPUT = "[data-test='username']"
PASSWORD_INPUT = "[data-test='password']"
LOGIN_BUTTON = "[data-test='login-button']"
ERROR_MESSAGE = "[data-test='error']"

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

def load(self):
self.navigate("https://www.saucedemo.com/")

def enter_username(self, username: str):
self.page.locator(self.USERNAME_INPUT).fill(username)

def enter_password(self, password: str):
self.page.locator(self.PASSWORD_INPUT).fill(password)

def click_login(self):
self.page.locator(self.LOGIN_BUTTON).click()

def login(self, username: str, password: str):
self.enter_username(username)
self.enter_password(password)
self.click_login()

def get_error_message(self):
return self.page.locator(self.ERROR_MESSAGE).text_content()

def is_error_displayed(self):
return self.page.locator(self.ERROR_MESSAGE).is_visible()

4. conftest.py

import pytest
from playwright.sync_api import Page, Browser, BrowserContext
from pages.login_page import LoginPage

@pytest.fixture(scope="session")
def browser_type_launch_args():
return {"headless": True}

@pytest.fixture(scope="session")
def browser_context_args():
return {"viewport": {"width": 1920, "height": 1080}}

@pytest.fixture
def login_page(page: Page) -> LoginPage:
return LoginPage(page)

@pytest.fixture
def authenticated_page(login_page: LoginPage):
"""Fixture that logs in and returns the page object"""
login_page.load()
login_page.login("standard_user", "secret_sauce")
yield login_page.page

5. test_login.py

import pytest
from playwright.sync_api import Page, expect
from pages.login_page import LoginPage

class TestLogin:
"""Test cases for SauceDemo login functionality"""

def test_successful_login(self, login_page: LoginPage):
"""Test successful login with valid credentials"""
# Arrange
login_page.load()

# Act
login_page.login("standard_user", "secret_sauce")

# Assert
expect(login_page.page).to_have_url("https://www.saucedemo.com/inventory.html")
expect(login_page.page).to_have_title("Swag Labs")

# Additional assertions
inventory_container = login_page.page.locator("#inventory_container")
expect(inventory_container).to_be_visible()

shopping_cart = login_page.page.locator("#shopping_cart_container")
expect(shopping_cart).to_be_visible()

def test_login_with_invalid_username(self, login_page: LoginPage):
"""Test login with invalid username"""
# Arrange
login_page.load()

# Act
login_page.login("invalid_user", "secret_sauce")

# Assert
expect(login_page.page).to_have_url("https://www.saucedemo.com/")
expect(login_page.is_error_displayed()).to_be_truthy()

error_text = login_page.get_error_message()
assert "Epic sadface: Username and password do not match" in error_text

def test_login_with_invalid_password(self, login_page: LoginPage):
"""Test login with invalid password"""
# Arrange
login_page.load()

# Act
login_page.login("standard_user", "wrong_password")

# Assert
expect(login_page.page).to_have_url("https://www.saucedemo.com/")
expect(login_page.is_error_displayed()).to_be_truthy()

error_text = login_page.get_error_message()
assert "Epic sadface: Username and password do not match" in error_text

def test_login_with_locked_user(self, login_page: LoginPage):
"""Test login with locked out user"""
# Arrange
login_page.load()

# Act
login_page.login("locked_out_user", "secret_sauce")

# Assert
expect(login_page.page).to_have_url("https://www.saucedemo.com/")
expect(login_page.is_error_displayed()).to_be_truthy()

error_text = login_page.get_error_message()
assert "Epic sadface: Sorry, this user has been locked out." in error_text

def test_login_with_empty_credentials(self, login_page: LoginPage):
"""Test login with empty username and password"""
# Arrange
login_page.load()

# Act
login_page.click_login()

# Assert
expect(login_page.page).to_have_url("https://www.saucedemo.com/")
expect(login_page.is_error_displayed()).to_be_truthy()

error_text = login_page.get_error_message()
assert "Epic sadface: Username is required" in error_text

@pytest.mark.parametrize("username,password", [
("standard_user", "secret_sauce"),
("problem_user", "secret_sauce"),
("performance_glitch_user", "secret_sauce"),
])
def test_login_multiple_users(self, login_page: LoginPage, username, password):
"""Test login with multiple valid users"""
# Arrange
login_page.load()

# Act
login_page.login(username, password)

# Assert
expect(login_page.page).to_have_url("https://www.saucedemo.com/inventory.html")
expect(login_page.page.locator(".app_logo")).to_have_text("Swag Labs")

# Cleanup - logout for next test
login_page.page.locator("#react-burger-menu-btn").click()
login_page.page.locator("#logout_sidebar_link").click()

def test_navigation_after_login(self, authenticated_page: Page):
"""Test that user can navigate after successful login"""
page = authenticated_page

# Test navigation to cart
cart_icon = page.locator("#shopping_cart_container")
cart_icon.click()
expect(page).to_have_url("https://www.saucedemo.com/cart.html")

# Test back to products
continue_shopping = page.locator("#continue-shopping")
continue_shopping.click()
expect(page).to_have_url("https://www.saucedemo.com/inventory.html")

6. pytest.ini

[pytest]
addopts =
--verbose
--strict-markers
--tb=short
--capture=no
--disable-warnings
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
markers =
smoke: Smoke tests
regression: Regression tests
login: Login related tests

7. 运行测试的脚本 run_tests.py

import subprocess
import sys

def run_tests():
"""Run pytest with appropriate arguments"""
# Install required packages
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])

# Install Playwright browsers
subprocess.check_call([sys.executable, "-m", "playwright", "install", "chromium"])

# Run tests
result = subprocess.run([
sys.executable, "-m", "pytest",
"tests/test_login.py::TestLogin::test_successful_login",
"-v",
"--headed"# Remove this to run in headless mode
])

return result.returncode

if __name__ == "__main__":
sys.exit(run_tests())

8. 快速运行指南

安装依赖:

pip install -r requirements.txt
playwright install chromium

运行所有测试:

pytest tests/test_login.py -v

运行特定测试:

pytest tests/test_login.py::TestLogin::test_successful_login -v

运行带UI的测试:

pytest tests/test_login.py --headed

主要特点:

  1. POM设计模式:页面对象分离,易于维护
  2. 稳定的定位器:优先使用data-test属性
  3. 全面的断言:包含URL、标题、元素可见性等断言
  4. 参数化测试:支持多用户登录测试
  5. 独立的fixture:提供已认证的page fixture
  6. 清晰的测试结构:遵循Arrange-Act-Assert模式
  7. 错误处理:包含各种错误场景的测试

代码可以直接复制运行,只需安装依赖并运行python run_tests.py即可。

### 使用 Python PlaywrightPytest 进行自动化测试 #### 安装必要的库 为了构建基于 PythonUI 自动化测试框架,需安装多个依赖项。这些工具包括用于编写和运行测试的 `pytest` 及其扩展模块 `pytest-playwright`,以及浏览器自动化的核心组件 `playwright`。 ```bash pip install pytest playwright pytest-playwright ``` 对于 Chromium 浏览器的支持,则通过命令来完成相应驱动程序的下载: ```bash python -m playwright install chromium ``` 此操作会确保本地环境中具备执行自动化脚本所需的全部资源[^1]。 #### 编写第一个测试案例 创建简单的登录功能验证示例可以更好地理解整个流程。下面是一个基本结构,在这里定义了一个名为 `test_login.py` 文件中的函数用来模拟用户输入用户名密码并提交表单的过程。 ```python import pytest def test_successful_login(page): page.goto("http://example.com/login") # 替换成实际网址 page.fill("#username", "admin") page.fill("#password", "secret") page.click("button[type='submit']") assert page.url == "http://example.com/dashboard" ``` 上述代码片段展示了如何利用 Page 对象提供的方法与网页交互,并最终断言页面跳转到了预期的目标地址以确认登录成功[^2]。 #### 配置报告生成 为了让测试结果更加直观易读,推荐集成 Allure 报告生成功能。这不仅有助于团队成员之间分享测试进展,也方便定位失败原因。首先得安装 allure-pytest 插件: ```bash pip install allure-pytest ``` 接着可以在命令行中指定参数让 pytest 输出 allure 格式的日志文件夹路径: ```bash pytest --alluredir=./results tests/ ``` 最后借助于独立发布的 Allure 命令行工具或者 Jenkins 等 CI 平台上的插件来进行可视化展示[^3]。 #### 实际应用场景下的注意事项 当面对更复杂的业务逻辑时,建议采用 YAML 或 JSON 文件存储测试数据,使维护变得更加轻松;同时考虑引入 fixture 来管理前后置条件,提高代码复用率。另外,针对不同环境可能存在的差异(比如开发、预发布),应当灵活调整配置选项以适应变化的需求[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值