在构建可维护的UI自动化测试框架过程中,采用合理的代码分层结构至关重要。本文介绍一种基于Selenium与Unittest的四层架构模式,提升测试脚本的复用性与可读性。
项目结构概览
该架构包含四个核心层级:
- Base层:封装浏览器底层操作
- Page层:定义页面业务逻辑
- Data层:管理测试数据
- Case层:组织测试用例执行流程
Base层 - 浏览器操作抽象
此层提供通用UI交互方法,降低上层对Selenium原生API的依赖。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver import ActionChains
class ElementHandler:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# 启动浏览器并访问URL
def launch(self, address):
self.driver.get(address)
self.driver.maximize_window()
# 定位元素
def locate(self, method, locator):
return self.driver.find_element(method, locator)
# 元素点击
def tap(self, method, locator):
element = self.locate(method, locator)
element.click()
# 输入文本(自动清除只读属性)
def type_text(self, method, locator, content):
field = self.locate(method, locator)
self.driver.execute_script("arguments[0].removeAttribute('readonly')", field)
field.clear()
field.send_keys(content)
# 模拟键盘操作
def press_key(self, method, locator, key):
self.locate(method, locator).send_keys(key)
# 双击操作
def double_tap(self, method, locator):
ActionChains(self.driver).double_click(
self.locate(method, locator)
).perform()
# 显式等待
def pause(self, seconds):
self.driver.implicitly_wait(seconds)
# 切换至新标签页
def switch_to_new_tab(self):
self.driver.switch_to.new_window('tab')
# 关闭浏览器
def close_browser(self):
self.driver.quit()
Page层 - 页面行为建模
将具体业务流程封装为类方法,提高用例编写效率。
LoginPage.py
from Base.core import ElementHandler
from selenium.webdriver.common.by import By
class LoginPage(ElementHandler):
# 环境配置
STAGING_URL = "https://test.kritycat.com"
UAT_URL = "https://uat.kritycat.com"
USERNAME = "admin"
PASSWORD = "secret"
def login_to_staging(self):
self.launch(self.STAGING_URL)
self._execute_login()
def login_to_uat(self):
self.launch(self.UAT_URL)
self._execute_login()
def _execute_login(self):
self.type_text(By.ID, "user", self.USERNAME)
self.type_text(By.ID, "password", self.PASSWORD)
self.tap(By.ID, "login")
self.pause(5)
FormPage.py
from Base.core import ElementHandler
from selenium.webdriver.common.by import By
class FormProcessor(ElementHandler):
def fill_form_id(self, form_id):
self.tap(By.ID, "Type")
self.type_text(By.XPATH, '//*[@id="Type"]/div/div/input', form_id)
Data层 - 数据驱动支持
使用YAML文件存储测试参数,实现数据与逻辑分离。
form_data.yaml
-
form_id: "0001"
Case层 - 测试执行入口
通过unittest组织测试套件,并利用DDT实现数据驱动。
import unittest
from ddt import ddt, file_data
from selenium import webdriver
from Page.LoginPage import LoginPage
from Page.FormPage import FormProcessor
@ddt
class FunctionalTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.browser = webdriver.Chrome()
cls.login_page = LoginPage(cls.browser)
cls.form_task = FormProcessor(cls.browser)
def test_login_process(self):
self.login_page.login_to_staging()
@file_data("../Data/form_data.yaml")
def test_form_submission(self, form_id):
self.form_task.fill_form_id(form_id)
@classmethod
def tearDownClass(cls):
cls.browser.quit()
分层优势说明
- 当登录地址变更时,仅需修改LoginPage中的常量值
- 测试数据调整无需改动代码,只需更新对应YAML文件
- 公共操作抽象后可在多个用例中重复调用
- 各层职责清晰,便于团队协作与后期维护