Python Selenium 自动化测试设计模式
一、页面对象模型(Page Object Model, POM)—— 基石模式
✅ 核心思想
将每个 Web 页面封装为独立对象,分离页面元素定位与测试逻辑,实现高内聚、低耦合。
📁 推荐项目结构
project/
├── base/ # 基础层(二次封装)
│ └── base_page.py
├── pages/ # 对象层(页面对象)
│ ├── login_page.py
│ └── dashboard_page.py
├── workflows/ # 逻辑层(业务流程封装)
│ └── user_workflow.py
├── tests/ # 业务层(测试用例)
│ └── test_login.py
├── utils/ # 工具层
│ ├── logger.py
│ └── screenshot.py
└── config.py # 配置管理
1. 基础层 base/base_page.py
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import logging, os, datetime
class BasePage:
def __init__(self, driver: WebDriver, timeout=15):
self.driver = driver
self.timeout = timeout
self.logger = logging.getLogger(__name__)
def wait_and_find(self, locator, desc="element"):
"""智能等待 + 元素查找(含日志与异常处理)"""
try:
WebDriverWait(self.driver, self.timeout).until(
EC.presence_of_element_located(locator)
)
ele = self.driver.find_element(*locator)
self.logger.info(f"✓ 找到元素 [{desc}]")
return ele
except Exception as e:
self._capture_screenshot(desc)
self.logger.error(f"✗ 未找到元素 [{desc}]: {str(e)}")
raise
def click(self, locator, desc="button"):
ele = self.wait_and_find(locator, desc)
ele.click()
self.logger.info(f"✓ 点击 [{desc}]")
def input_text(self, locator, text, desc="input"):
ele = self.wait_and_find(locator, desc)
ele.clear()
ele.send_keys(text)
self.logger.info(f"✓ 输入 [{desc}]: {text}")
def _capture_screenshot(self, name):
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
path = f"screenshots/{name}_{timestamp}.png"
os.makedirs("screenshots", exist_ok=True)
self.driver.save_screenshot(path)
self.logger.info(f"📸 截图保存: {path}")
2. 对象层 pages/login_page.py
from selenium.webdriver.common.by import By
from base.base_page import BasePage
class LoginPage(BasePage):
# 元素定位器集中管理(便于维护)
USERNAME = (By.ID, "username")
PASSWORD = (By.ID, "password")
LOGIN_BTN = (By.ID, "loginButton")
ERROR_MSG = (By.CLASS_NAME, "error-message")
def __init__(self, driver):
super().__init__(driver)
self.driver.get("https://example.com/login") # 可选:自动跳转
def enter_username(self, username):
self.input_text(self.USERNAME, username, "用户名框")
return self # 支持链式调用
def enter_password(self, password):
self.input_text(self.PASSWORD, password, "密码框")
return self
def click_login(self):
self.click(self.LOGIN_BTN, "登录按钮")
return self
def login(self, username, password):
"""封装完整登录流程(原子操作组合)"""
return (self.enter_username(username)
.enter_password(password)
.click_login())
def get_error_message(self):
return self.wait_and_find(self.ERROR_MSG, "错误提示").text
3. 测试用例 tests/test_login.py
import pytest
from selenium import webdriver
from pages.login_page import LoginPage
class TestLogin:
@pytest.fixture(autouse=True)
def setup(self):
self.driver = webdriver.Chrome()
yield
self.driver.quit()
def test_valid_login(self):
login_page = LoginPage(self.driver)
login_page.login("admin", "secure123")
# 验证跳转(可结合DashboardPage进一步验证)
assert "dashboard" in self.driver.current_url
def test_invalid_login(self):
login_page = LoginPage(self.driver)
login_page.login("wrong", "pass")
assert "用户名或密码错误" in login_page.get_error_message()
二、可加载组件模式(Loadable Component)—— 状态保障
✅ 核心思想
确保页面在操作前已完全加载且状态正确,避免因加载时序导致的测试不稳定。
💡 Python 实现方案(Selenium 无内置,需自定义)
from abc import ABC, abstractmethod
from selenium.common.exceptions import TimeoutException
class LoadableComponent(ABC):
"""可加载组件基类(Python版实现)"""
def __init__(self, driver):
self.driver = driver
self.is_loaded_flag = False
def open(self):
"""统一入口:加载页面 + 验证状态"""
self.load()
try:
self.is_loaded()
self.is_loaded_flag = True
logging.info(f"✓ {self.__class__.__name__} 加载成功")
except AssertionError as e:
logging.error(f"✗ {self.__class__.__name__} 加载失败: {e}")
raise
return self
@abstractmethod
def load(self):
"""子类实现:导航到页面"""
pass
@abstractmethod
def is_loaded(self):
"""子类实现:验证页面加载完成(断言关键元素/URL)"""
pass
# 应用示例:继承 LoadableComponent 的登录页
class SafeLoginPage(LoadableComponent, BasePage): # 多继承
URL = "https://example.com/login"
TITLE_KEYWORD = "登录"
def load(self):
self.driver.get(self.URL)
def is_loaded(self):
# 双重验证:URL + 标题
assert self.URL in self.driver.current_url, "URL 不匹配"
assert self.TITLE_KEYWORD in self.driver.title, "标题验证失败"
# 可补充:关键元素可见性检查
self.wait_and_find((By.ID, "loginForm"), "登录表单")
# 保留原有操作方法...
def login(self, user, pwd):
if not self.is_loaded_flag:
self.open() # 自动触发加载验证
# ...执行登录操作
🌟 优势
防错机制:操作前自动验证页面状态
调试友好:失败时明确提示“加载失败”而非“元素未找到”
提升稳定性:解决动态内容加载导致的时序问题
三、机器人模式(Robot Pattern)—— 业务语义封装
✅ 核心思想
将测试步骤封装为高可读性、业务导向的命令,使测试脚本如自然语言般清晰。
💡 实现方案:工作流层 workflows/user_workflow.py
class UserRobot:
"""用户操作机器人:封装跨页面业务流程"""
def __init__(self, driver):
self.driver = driver
self.login_page = None
self.dashboard_page = None
def login_as_admin(self):
"""高级别业务命令:登录管理员账号"""
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage
self.login_page = LoginPage(self.driver).open() # 结合LoadableComponent
self.login_page.login("admin", "secure123")
# 自动跳转并验证仪表盘
self.dashboard_page = DashboardPage(self.driver).open()
return self.dashboard_page # 返回目标页面对象
def search_product(self, keyword):
"""封装搜索全流程(含结果验证)"""
self.dashboard_page.search_bar.input(keyword)
self.dashboard_page.search_button.click()
assert keyword in self.driver.title, "搜索结果页标题异常"
return self.dashboard_page.get_search_results()
def logout(self):
self.dashboard_page.click_settings().click_logout()
assert "login" in self.driver.current_url
📝 测试脚本示例(极致可读性)
def test_admin_workflow():
robot = UserRobot(driver)
# 脚本如业务描述:
dashboard = robot.login_as_admin()
results = robot.search_product("Selenium设计模式")
assert len(results) > 0
robot.logout()
logging.info("✅ 管理员全流程测试通过")
🔑 综合最佳实践建议
💎 总结
POM 是骨架:解决元素定位与操作的封装问题
Loadable Component 是安全锁:保障页面状态可信,提升测试鲁棒性
Robot Pattern 是语言翻译器:将技术操作转化为业务语言,提升脚本可读性与协作效率
黄金组合:
POM(页面细节) + LoadableComponent(状态保障) + Robot(业务语义)
三者协同,可构建出高可维护、高稳定性、高可读性的企业级自动化测试体系。建议从小型项目开始实践分层,逐步完善工具链,避免过度设计。
Python Selenium 自动化测试设计模式
https://blog.cikaros.cn/archives/python-selenium-zi-dong-hua-ce-shi-she-ji-mo-shi