Web 自动化架构:POM 分层设计的深度实践
在 Web 自动化由「脚本堆砌」转向「工程化框架」的过程中,POM(Page Object Model) 是最核心的分水岭。它不仅是为了代码整洁,更是为了应对现代 Web 应用频繁迭代带来的维护压力。
一、 核心痛点:为什么拒绝「裸奔」的脚本?
直接在测试函数中硬编码定位符(如 driver.find_element(By.ID, "login"))是危险的工程债务。一旦前端修改了一个 ID,你可能需要全局重构数百个测试文件。
POM 的核心价值:
- 解耦: 将「如何操作页面」与「业务断言逻辑」分离。
- 抽象: 将复杂的 UI 转换成简单的业务动词(如
perform_login)。 - 复用: 定义一次页面对象,即可在无数个场景中反复调度。
二、 三层分层架构:Page-Action-Test
为了实现最大柔性,我们通常将框架拆解为三层:
| 层级 | 职责 (Responsibility) | 核心产物 |
|---|---|---|
| 基础层 (Base) | 封装底层通信与显式等待逻辑。 | base_page.py |
| 业务层 (Page) | 映射 DOM 元素并封装原子操作/业务流。 | login_page.py |
| 用例层 (Test) | 专注于业务流串联与最终结果断言。 | test_auth.py |
三、 深度实践:从地基到上层
1. 基础层 (BasePage):稳定性的来源
基础层不应感知具体的页面业务逻辑,它只负责「如何稳如泰山地交互」。
PYTHON
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage:
def __init__(self, driver, timeout=10):
self.driver = driver
self.wait = WebDriverWait(driver, timeout)
def find_visible_element(self, locator):
"""精准捕捉:仅在元素真正可见时返回"""
return self.wait.until(EC.visibility_of_element_located(locator))
def safe_input(self, locator, text):
ele = self.find_visible_element(locator)
ele.clear()
ele.send_keys(text)
2. 业务层 (PageObject):定位与动作的合集
通过把定位符(Locators)与操作方法(Methods)封装在一起,构建「业务黑盒」。
PYTHON
from selenium.webdriver.common.by import By
class LoginPage(BasePage):
# 定位符:作为类属性集中管理
_USERNAME = (By.ID, 'txtUName')
_PASSWORD = (By.ID, 'txtPassword')
_LOGIN_BTN = (By.ID, 'btnLogin')
def login_as(self, user, pwd):
"""业务语义化封装"""
self.safe_input(self._USERNAME, user)
self.safe_input(self._PASSWORD, pwd)
self.click_element(self._LOGIN_BTN)
四、 用例层 (TestCase):可读性即是正义
得益于 POM 的封装,我们的用例层代码应具备极高的可读性,甚至非开发人员也能看懂业务流:
PYTHON
def test_login_with_invalid_credentials(driver):
# 实例化页面对象,进入业务逻辑
login = LoginPage(driver)
login.open("http://example.com/login")
# 链式操作或单步操作
login.login_as("invalid_user", "wrong_pass")
# 断言结果
assert login.has_error_message("手机号或密码错误")
五、 回顾与后续进阶
POM 并非终点,而是起点。当页面对象数量激增后,我们将面临「数据驱动」的需求。接下来我们将探讨如何将 YAML 与 POM 结合,实现更强大的 DDT。