python
import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
os.environ['WDM_LOCAL'] = '1' # 强制使用本地缓存的驱动
os.environ['WDM_SKIP_UPDATE'] = '1' # 跳过版本更新检查
class BasePage(object):
def __init__(self):
self.driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install())) #自动下载对应的驱动
def get_url(self,url):
self.driver.get(url)
def quit_browser(self):
self.driver.quit()
def send_keys(self,selector,context):
self.driver.find_element(*selector).send_keys(context)
def click(self,selector):
self.driver.find_element(*selector).click()
python
from time import sleep
from selenium.webdriver.common.by import By
from day0320.pom.base.BasePage import BasePage
class LoginPage(BasePage):
# 1.定义所有元素
hc_username = (By.XPATH, "//*[@id='txtUName']") #登录文本框
hc_password = (By.XPATH, "//*[@id='txtPassword']") #密码框
hc_loginBtn = (By.XPATH, "//*[@id='btnLogin']") #按钮
hc_quit = (By.XPATH, "//*[@id='headerUserInfo']/span/a[2]") #退出按钮
#告诉代码 你在做什么
def do_login(self, username, password):
self.send_keys(self.hc_username,username)
self.send_keys(self.hc_password,password)
self.click(self.hc_loginBtn)
def quit_login(self):
self.click(self.hc_quit)
# pom 本质把重复的操作独立的提取出去
#页面的操作--它会有很多操作
#页面操作都是固定的 元素会变 操作不会变
#打开浏览器
#关闭浏览器
#输入文字
#点击 (可以把所有的操作提取出去)
if __name__ == '__main__':
lp = LoginPage()
lp.get_url("http://novel.hctestedu.com/user/login.html")
sleep(3)
lp.do_login("13887310226","123456")
sleep(5)
lp.quit_browser()
一、POM(Page Object Model)框架核心思想
POM(页面对象模型)是 Selenium 自动化测试中最主流的框架设计模式,核心是:
- 页面与逻辑分离:每个页面封装成一个类,包含页面元素定位和操作方法;
- 复用性高:元素定位只写一次,修改时只需改一处;
- 可读性强:测试用例只关注业务逻辑,不关注元素定位细节;
- 易维护:页面结构变化时,只需修改对应页面类,不影响测试用例。
二、完整的 Selenium POM 框架封装(实战版)
以下是一套可直接复用的 POM 框架结构,包含基础封装、页面封装、测试用例三层架构:
python
pom_project/
├── base/ # 基础层:封装通用方法
│ ├── __init__.py
│ └── BasePage.py # 封装驱动初始化、元素操作(点击/输入/等待等)
├── page/ # 页面层:封装各页面的元素和操作
│ ├── __init__.py
│ ├── LoginPage.py # 登录页
│ └── HomePage.py # 首页(示例)
├── test_case/ # 测试用例层:编写具体测试逻辑
│ ├── __init__.py
│ └── test_login.py # 登录测试用例
├── utils/ # 工具层:封装辅助功能(可选)
│ ├── __init__.py
│ └── config.py # 配置文件(如URL、账号密码)
└── run.py # 运行入口
1. 核心优势
- 低耦合:页面元素 / 操作和测试用例分离,修改页面结构只需改对应 Page 类;
- 高复用:多个用例可复用同一个 Page 类的方法(如登录操作);
- 易扩展:新增页面时,只需新增一个 Page 类,继承 BasePage 即可;
- 可读性强 :测试用例像自然语言,比如
login_page.login(phone, pwd)一眼就能看懂。
2. 进阶拓展(可选)
- 添加日志:在 BasePage 中封装日志打印,方便排查问题;
- 数据驱动:结合 Excel/CSV/YAML 管理测试数据(如多组账号密码);
- 测试报告:集成 pytest+allure 生成可视化测试报告;
- 异常处理:在 BasePage 中添加通用异常捕获,比如元素定位失败时截图;
- 多浏览器支持:在 BasePage 中添加 Chrome/Firefox/Edge 的驱动切换逻辑。
总结
- POM 核心:将页面元素、操作封装为 Page 类,测试用例只调用 Page 类的方法,实现 "页面与逻辑分离"。
- 框架结构:基础层(BasePage)→ 页面层(LoginPage/HomePage)→ 测试用例层(test_login),三层架构清晰易维护。
- 关键封装:BasePage 中封装通用操作(定位 / 点击 / 输入 / 等待),所有 Page 类继承它,避免重复代码。
这套框架可以直接适配你的自动化测试场景,只需替换config.py中的 URL、元素定位符,以及 Page 类中的业务逻辑即可。
conftest 单独封装、流程化 都继承父类conftest

python
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
driver = None
@pytest.fixture(scope="session")
def browser():
global driver
if driver is None:
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install())) # 自动下载对应的驱动
driver.maximize_window()
yield driver
driver.quit()
python
import os
import yaml
import pytest
from day0320.pom.page.LoginPage2 import LoginPage
from day0320.pom.page.BookDetilsPage import BookDetilsPage
from time import sleep
#经过了用例1-->用例2-->用例3#没有关闭浏览器,没有创建新的driver
curPath = os.path.dirname(os.path.realpath(__file__)) #获取当前脚本所在文件夹路径
ymlPath = os.path.join(curPath, "data.yaml") #获取yaml文件路径
f = open(ymlPath, "r")
d = yaml.safe_load(f.read())
@pytest.mark.parametrize("caseinfo",d["login"])
def test_01(browser,caseinfo):
lp = LoginPage(browser)
lp.get_url("http://novel.hctestedu.com/user/login.html")
sleep(5)
lp.do_login(caseinfo["username"],caseinfo["password"])
sleep(3)
lp.quit_login()
def test_02(browser):
pass
def test_03(browser):
db = BookDetilsPage(browser)
db.get_url("http://novel.hctestedu.com/book/198.html")
sleep(5)
db.read_click()
sleep(5)
if __name__ == '__main__':
pytest.main(["-v","-s"])