1. 项目概述:为什么我们需要一个自动化框架?

如果你用Python写过Selenium脚本,大概率经历过这样的场景:打开浏览器,找到元素,输入内容,点击按钮,然后写个 assert 验证结果。一两个脚本还好,当你要测几十个页面、几百个用例时,代码很快就变成了一团乱麻——元素定位字符串散落在各个角落,浏览器驱动路径硬编码在文件里,用例之间充斥着重复的 time.sleep ,一旦页面改个ID,你得翻遍所有文件去修改。这根本不是可持续的工程实践,而是脚本小子式的“一次性玩具”。

所以,我们今天聊的不是“怎么用Selenium的 find_element_by_id ”,而是 如何用工程化的思想,在Python中构建一个健壮、可维护、可扩展的Selenium自动化框架 。这就像盖房子,零散的脚本是砖头,而框架是设计图纸、施工规范和质检流程。它能让你的自动化测试从“勉强能用”升级为“稳定可靠的生产力工具”,无论是用于Web UI自动化测试、数据抓取,还是日常的重复性网页操作,都能事半功倍。

这个框架的核心目标很明确: 降低维护成本、提升执行效率、增强用例稳定性 。接下来,我会带你从零开始,拆解一个典型框架的每一层设计,并分享我在多个项目中趟过的坑和总结的最佳实践。

2. 框架核心架构与设计哲学

一个成熟的自动化框架绝不是把代码堆在一起就行。它需要清晰的分层和职责分离。参考经典的测试自动化架构,并结合Python生态的特点,我通常采用以下四层结构,这也是业界公认比较合理的一种模式。

2.1 基础层:封装与抽象

这一层是框架的基石,目的是将Selenium原生API中不稳定、易变的部分封装起来,提供一套稳定、统一的接口给上层调用。

核心组件:

  1. 浏览器驱动管理 :手动下载驱动、指定路径是新手常踩的坑。基础层应该能自动根据系统环境、浏览器版本下载匹配的WebDriver。我们可以使用 webdriver-manager 这个第三方库,它能完美解决这个问题。

    # 不好的做法:硬编码路径
    driver = webdriver.Chrome(executable_path=r'C:\chromedriver.exe')
    
    # 好的做法:自动管理
    from webdriver_manager.chrome import ChromeDriverManager
    from selenium import webdriver
    service = webdriver.chrome.service.Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=service)
    

    注意 webdriver-manager 会检查缓存和本地版本,避免重复下载,并且能自动匹配最新的浏览器驱动,省心省力。

  2. 元素操作封装 :Selenium的 find_element click send_keys 很基础,但缺乏健壮性。我们需要封装一个“智能等待”和“重试机制”的查找方法。

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.common.exceptions import TimeoutException, StaleElementReferenceException
    
    class BasePage:
        def __init__(self, driver):
            self.driver = driver
            self.wait = WebDriverWait(driver, 10, poll_frequency=0.5)
    
        def find_element(self, locator):
            """封装查找元素,加入显式等待和异常处理"""
            try:
                # 等待元素可见且可交互
                element = self.wait.until(EC.element_to_be_clickable(locator))
                return element
            except TimeoutException:
                # 可以在这里自动截图,记录日志
                self.logger.error(f"元素定位超时: {locator}")
                self.driver.save_screenshot("timeout_error.png")
                raise
    

    这个 find_element 方法内部使用了 WebDriverWait ,它会持续尝试查找元素直到成功(或超时),避免了因为页面加载慢或JS渲染导致的 NoSuchElementException 。这是提升脚本稳定性的最关键一步。

  3. 日志与报告集成 :框架必须要有“眼睛”。使用Python内置的 logging 模块,为每个操作记录信息、警告和错误。同时,要预留接口,方便与 Allure HTMLTestRunner 等报告工具集成。

2.2 页面对象层:PO模式实践

Page Object (PO) 模式是UI自动化的灵魂。它的核心思想是 将页面元素定位和页面操作行为封装成一个类 。一个页面(或一个页面片段)对应一个类,类属性是元素定位器,类方法是页面操作。

为什么一定要用PO? 假设登录按钮的定位器从 id=“loginBtn” 改成了 class=“btn-login””。如果没有PO,你需要在所有用到这个按钮的测试用例里逐一修改。如果用了PO,你只需要在 LoginPage 这个类里修改一次 login_button_loc`这个属性。

一个标准的页面对象类示例:

# login_page.py
from .base_page import BasePage
from selenium.webdriver.common.by import By

class LoginPage(BasePage):
    # 1. 定义元素定位器(集中管理,一目了然)
    USERNAME_INPUT = (By.ID, 'username')
    PASSWORD_INPUT = (By.ID, 'password')
    LOGIN_BUTTON = (By.XPATH, '//button[@type="submit"]')
    ERROR_MSG = (By.CLASS_NAME, 'error-message')

    # 2. 页面操作方法
    def enter_username(self, username):
        self.find_element(self.USERNAME_INPUT).send_keys(username)
        return self # 支持链式调用

    def enter_password(self, password):
        self.find_element(self.PASSWORD_INPUT).send_keys(password)
        return self

    def click_login(self):
        self.find_element(self.LOGIN_BUTTON).click()

    # 3. 组合业务流方法
    def login(self, username, password):
        """完整的登录业务流程"""
        self.enter_username(username).enter_password(password).click_login()

    # 4. 页面状态断言方法
    def get_error_message(self):
        return self.find_element(self.ERROR_MSG).text

实操心得 :在PO类中返回 self (即对象本身)是一个小技巧,它允许你进行链式调用,比如 page.enter_username(‘admin’).enter_password(‘123’).click_login() ,让代码更流畅。此外,不要把断言写在PO层,PO只负责行为和状态获取,断言应该留在测试用例层。

2.3 数据驱动层:让用例与数据分离

数据驱动测试是另一个关键理念。它的目标是将 测试逻辑 测试数据 分离。这样,同一套测试逻辑,可以用不同的数据组合来执行,极大提高了用例的复用性和可维护性。

常见的数据源管理方式:

  1. YAML文件 :结构清晰,可读性好,非常适合管理配置和测试数据。例如,我们可以把不同用户的登录数据放在YAML里。
    # test_data/login_data.yaml
    - username: "admin"
      password: "correct_password"
      expected: "login_success"
    
    - username: "admin"
      password: "wrong_password"
      expected: "error_invalid_password"
    
    - username: ""
      password: "some_password"
      expected: "error_username_required"
    
  2. JSON文件 :与YAML类似,也是常见选择,尤其适合嵌套复杂的数据结构。
  3. Excel/CSV :非技术背景的团队成员(如产品经理、业务分析师)可能更习惯用Excel来维护测试数据。
  4. 数据库 :当测试数据需要动态生成或与其他系统共享时,从数据库读取是更专业的选择。

在框架中集成数据驱动: 我们可以使用 ddt (Data-Driven Tests)库,它能够与 unittest pytest 完美结合,优雅地实现数据驱动。

import ddt
import unittest
import yaml
from login_page import LoginPage

@ddt.ddt
class TestLogin(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.login_page = LoginPage(cls.driver)

    @ddt.file_data('test_data/login_data.yaml')
    def test_login(self, username, password, expected):
        self.login_page.login(username, password)
        if "error" in expected:
            actual_error = self.login_page.get_error_message()
            self.assertIn(expected.split('_')[-1], actual_error.lower())
        else:
            # 验证登录成功,例如检查是否跳转到首页
            self.assertIn("dashboard", self.driver.current_url)

使用 @ddt.file_data 装饰器,框架会自动加载YAML文件中的每一组数据,并作为单独一个测试用例来运行。在测试报告中,你会看到 test_login_1 , test_login_2 等,非常清晰。

2.4 测试用例与执行层

这是框架的“调度中心”,负责组织测试套件、管理测试生命周期(setup/teardown)、生成报告和处理异常。

测试运行器的选择:

  1. unittest :Python标准库,入门简单,生态成熟。配合 HTMLTestRunner 可以生成美观的HTML报告。它的 setUp / tearDown 方法提供了标准的测试夹具。
  2. pytest :目前更主流的测试框架,功能更强大灵活。夹具(fixture)系统更优秀,插件生态丰富(如 pytest-html 生成报告, pytest-xdist 分布式执行),断言写法也更直观。
    # 使用pytest fixture管理driver
    import pytest
    @pytest.fixture(scope="session") # 整个测试会话只启动一次浏览器
    def driver():
        driver = webdriver.Chrome()
        yield driver
        driver.quit() # 所有测试结束后关闭浏览器
    
    def test_login_success(driver):
        page = LoginPage(driver)
        page.login("admin", "123456")
        assert "welcome" in driver.page_source
    
    我个人更推荐pytest ,它的灵活性和可扩展性远超unittest,是现代Python自动化测试的首选。

报告生成: 一份好的测试报告能直观反映问题。除了基本的 pytest-html Allure 框架能生成极其详细和美观的交互式报告,包含步骤截图、测试描述、严重等级等,是向团队展示自动化成果的利器。

3. 关键实现细节与避坑指南

框架搭好了架子,但魔鬼在细节里。下面这些点,是决定你的框架是“花架子”还是“战斗机”的关键。

3.1 元素定位策略与等待机制

定位策略优先级:

  1. ID :唯一且稳定,优先级最高。
  2. Name :通常也较稳定。
  3. CSS Selector :性能好,语法灵活,是复杂定位的首选。例如 input.btn-primary
  4. XPath :功能最强大,可以遍历整个DOM,但性能相对较差,且容易因页面结构微小变动而失效。 慎用绝对路径 (以 / 开头),尽量使用相对路径和属性结合。
    • 不好: /html/body/div[3]/div[2]/form/input[1]
    • 好: //input[@id='username' and @type='text']

等待机制是稳定的生命线:

  • 强制等待 ( time.sleep ):除非万不得已(如等待一个非网页元素的第三方组件),否则禁止使用。它是脚本不稳定和执行缓慢的元凶。
  • 隐式等待 ( driver.implicitly_wait ):设置一个全局的等待时间,在查找任何元素时,如果没立刻找到,会轮询等待。问题在于它不灵活,并且对于某些条件(如元素可点击)无效。
  • 显式等待 ( WebDriverWait ): 必须掌握的核心技能 。它允许你为某个特定的条件等待,条件满足则立即继续,超时则报错。
    from selenium.webdriver.support import expected_conditions as EC
    # 等待元素可见
    wait.until(EC.visibility_of_element_located((By.ID, "slowLoadingElement")))
    # 等待元素可点击
    wait.until(EC.element_to_be_clickable((By.NAME, "submitBtn")))
    # 等待元素消失
    wait.until(EC.invisibility_of_element_located((By.CLASS_NAME, "loadingMask")))
    
    将显式等待封装在你的基础 find_element 方法中,是解决“元素找不到”问题的最有效手段。

3.2 处理动态内容与iframe

动态加载/AJAX内容 :一个元素可能因为数据异步加载而稍后才出现。此时,单纯的 presence_of_element_located (元素存在于DOM)可能不够,需要结合 visibility_of_element_located (元素可见)或自定义等待条件。

# 自定义等待条件:等待元素包含特定文本
def text_to_be_present_in_element(locator, text):
    def _predicate(driver):
        try:
            element_text = driver.find_element(*locator).text
            return text in element_text
        except StaleElementReferenceException:
            return False
    return _predicate

wait.until(text_to_be_present_in_element((By.ID, "status"), "加载完成"))

iframe/框架嵌套 :如果元素在iframe里,你必须先切换到对应的iframe上下文,才能操作其中的元素。

# 通过ID或Name切换
driver.switch_to.frame("iframe_id_or_name")
# 通过索引切换(从0开始)
driver.switch_to.frame(0)
# 通过WebElement切换
iframe_element = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe_element)

# 操作iframe内的元素...
driver.find_element(By.ID, "inner_button").click()

# 操作完成后,切回主文档
driver.switch_to.default_content()

常见坑 :操作完iframe内的元素后忘记切回主文档,导致后续查找元素失败。务必成对使用 switch_to.frame switch_to.default_content

3.3 测试数据与配置管理

不要把数据库连接字符串、账号密码、URL等硬编码在代码里。使用配置文件或环境变量。

推荐结构:

project/
├── config/
│   ├── settings.py    # 主配置文件,定义全局常量
│   └── dev_config.yaml # 环境相关配置(开发)
├── test_data/         # 存放YAML/JSON数据文件
├── pages/             # 页面对象类
├── tests/             # 测试用例
└── conftest.py        # pytest全局夹具配置

settings.py 示例:

import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

# 环境变量优先,其次读配置文件
BROWSER = os.getenv("TEST_BROWSER", "chrome")
BASE_URL = os.getenv("TEST_BASE_URL", "https://dev.example.com")
IMPLICIT_WAIT = int(os.getenv("IMPLICIT_WAIT", "10"))
SCREENSHOT_DIR = BASE_DIR / "reports" / "screenshots"
SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True) # 自动创建目录

3.4 异常处理与截图

自动化脚本在无人值守运行时,必须有完善的异常处理和证据保留机制。

全局异常捕获与截图: 可以在测试框架的 tearDown 方法或pytest的钩子函数中实现。

# 在unittest的TestCase中
def tearDown(self):
    if hasattr(self, '_outcome'): # Python 3.4+
        result = self._outcome.result
        if result and any(test for test, _ in result.errors + result.failures):
            # 如果测试失败或报错,截图
            timestamp = time.strftime("%Y%m%d_%H%M%S")
            screenshot_name = f"{self.id()}_{timestamp}.png"
            screenshot_path = SCREENSHOT_DIR / screenshot_name
            self.driver.save_screenshot(str(screenshot_path))
            self.logger.info(f"测试失败,截图已保存至: {screenshot_path}")
    super().tearDown()

处理弹窗和浏览器对话框:

# 处理JavaScript Alert
alert = driver.switch_to.alert
print(alert.text)
alert.accept() # 点击确定
# alert.dismiss() # 点击取消

# 处理浏览器原生的文件上传(非input type=file)
# 通常需要借助AutoIT、PyWin32等工具,或者更推荐让开发将上传组件改为input type=file

4. 从零搭建框架的实操步骤

理论说了这么多,我们动手搭一个最小可用的框架。这里以 pytest 为核心。

4.1 第一步:初始化项目与环境

# 1. 创建项目目录
mkdir my_auto_framework && cd my_auto_framework

# 2. 创建虚拟环境(强烈推荐)
python -m venv venv
# Windows激活
venv\Scripts\activate
# Mac/Linux激活
source venv/bin/activate

# 3. 安装核心依赖
pip install selenium webdriver-manager pytest pytest-html allure-pytest

4.2 第二步:构建目录结构与核心文件

my_auto_framework/
├── config/
│   ├── __init__.py
│   └── settings.py          # 配置文件
├── core/
│   ├── __init__.py
│   └── base_page.py         # 基础页面封装
├── pages/
│   ├── __init__.py
│   └── login_page.py        # 登录页面对象(示例)
├── test_data/
│   └── login_data.yaml      # 测试数据
├── tests/
│   ├── __init__.py
│   ├── conftest.py          # pytest共享夹具
│   └── test_login.py        # 测试用例
├── reports/                 # 报告目录(自动生成)
├── logs/                    # 日志目录(自动生成)
└── requirements.txt         # 依赖清单

core/base_page.py (框架核心):

import logging
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

class BasePage:
    """所有页面对象的基类"""
    def __init__(self, driver, timeout=10):
        self.driver = driver
        self.timeout = timeout
        self.wait = WebDriverWait(driver, timeout)
        self.logger = logging.getLogger(__name__)

    def find_element(self, locator):
        """查找单个元素(智能等待)"""
        self.logger.info(f"正在查找元素: {locator}")
        try:
            element = self.wait.until(EC.presence_of_element_located(locator))
            self.wait.until(EC.visibility_of(element)) # 额外等待可见
            return element
        except TimeoutException as e:
            self.logger.error(f"查找元素超时: {locator}")
            self._take_screenshot("element_not_found")
            raise e

    def find_elements(self, locator):
        """查找多个元素"""
        self.logger.info(f"正在查找多个元素: {locator}")
        try:
            elements = self.wait.until(EC.presence_of_all_elements_located(locator))
            return elements
        except TimeoutException:
            return [] # 没找到返回空列表,有时是符合预期的

    def click(self, locator):
        """点击元素"""
        element = self.find_element(locator)
        self.logger.info(f"点击元素: {locator}")
        element.click()

    def input_text(self, locator, text):
        """输入文本,先清空"""
        element = self.find_element(locator)
        element.clear()
        self.logger.info(f"向元素 {locator} 输入文本: {text}")
        element.send_keys(text)

    def get_text(self, locator):
        """获取元素文本"""
        element = self.find_element(locator)
        return element.text.strip()

    def _take_screenshot(self, name):
        """内部截图方法"""
        import time
        timestamp = time.strftime("%Y%m%d_%H%M%S")
        filename = f"{name}_{timestamp}.png"
        # 这里需要配置截图路径,建议放到配置文件中
        path = f"./reports/screenshots/{filename}"
        self.driver.save_screenshot(path)
        self.logger.info(f"截图已保存: {path}")

4.3 第三步:编写页面对象与测试用例

pages/login_page.py

from selenium.webdriver.common.by import By
from core.base_page import BasePage

class LoginPage(BasePage):
    # 元素定位器
    USERNAME_INPUT = (By.ID, "username")
    PASSWORD_INPUT = (By.ID, "password")
    LOGIN_BUTTON = (By.XPATH, "//button[contains(text(), '登录')]")
    ERROR_MSG = (By.CLASS_NAME, "alert-error")

    def login(self, username, password):
        """登录业务流程"""
        self.input_text(self.USERNAME_INPUT, username)
        self.input_text(self.PASSWORD_INPUT, password)
        self.click(self.LOGIN_BUTTON)
        return self # 支持链式调用

    def get_error_message(self):
        """获取错误提示信息"""
        try:
            return self.get_text(self.ERROR_MSG)
        except:
            return "" # 如果没有错误元素,返回空字符串

tests/conftest.py (pytest全局配置):

import pytest
import logging
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service

# 配置日志
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

@pytest.fixture(scope="session")
def driver():
    """全局浏览器驱动夹具"""
    logger = logging.getLogger(__name__)
    logger.info("正在启动Chrome浏览器...")
    # 使用webdriver-manager自动管理驱动
    service = Service(ChromeDriverManager().install())
    options = webdriver.ChromeOptions()
    options.add_argument('--headless') # 无头模式,不显示GUI,适合CI环境
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-dev-shm-usage')
    driver = webdriver.Chrome(service=service, options=options)
    driver.implicitly_wait(5) # 设置一个较小的全局隐式等待作为兜底
    driver.maximize_window()

    yield driver # 测试用例执行的地方

    logger.info("测试结束,关闭浏览器...")
    driver.quit()

@pytest.fixture
def login_page(driver):
    """登录页面夹具"""
    from pages.login_page import LoginPage
    # 假设登录页面的URL
    driver.get("https://example.com/login")
    return LoginPage(driver)

tests/test_login.py

import pytest
import yaml
import os

# 加载测试数据
def load_login_data():
    data_file = os.path.join(os.path.dirname(__file__), '..', 'test_data', 'login_data.yaml')
    with open(data_file, 'r', encoding='utf-8') as f:
        return yaml.safe_load(f)

class TestLogin:
    """登录功能测试集"""

    @pytest.mark.parametrize("data", load_login_data())
    def test_login_with_data_driven(self, login_page, data):
        """数据驱动登录测试"""
        username = data["username"]
        password = data["password"]
        expected = data["expected"]

        login_page.login(username, password)

        if expected == "success":
            # 验证登录成功,例如检查URL或页面元素
            assert "dashboard" in login_page.driver.current_url
        else:
            # 验证登录失败,提示信息正确
            error_msg = login_page.get_error_message()
            assert expected in error_msg.lower()

    def test_login_success(self, login_page):
        """正向用例:正确账号密码登录成功"""
        login_page.login("valid_user", "valid_pass")
        # 断言:登录后跳转到首页或出现用户菜单
        assert login_page.driver.current_url == "https://example.com/dashboard"
        # 或者检查欢迎语
        welcome_locator = (By.ID, "welcome-msg")
        assert "欢迎" in login_page.get_text(welcome_locator)

4.4 第四步:运行测试并生成报告

在项目根目录下创建 pytest.ini 配置文件:

[pytest]
# 指定测试文件路径
testpaths = tests
# 自动发现测试文件规则
python_files = test_*.py
# 自动发现测试类和函数规则
python_classes = Test*
python_functions = test_*
# 添加命令行参数
addopts = -v --html=reports/report.html --self-contained-html
# 日志级别
log_cli = true
log_cli_level = INFO

运行测试:

pytest
# 或者运行特定文件
pytest tests/test_login.py -v
# 生成Allure报告(需要先安装Allure命令行工具)
pytest --alluredir=./reports/allure_results
allure serve ./reports/allure_results

执行后,你会在 reports 目录下看到一个 report.html ,用浏览器打开就是一份详细的测试报告。

5. 常见问题排查与进阶技巧

框架跑起来了,但在实际项目中,你会遇到各种稀奇古怪的问题。这里我总结了一个“排坑手册”。

5.1 元素定位失败问题排查表

问题现象 可能原因 排查步骤与解决方案
NoSuchElementException 1. 元素定位器写错了。
2. 页面还没加载完。
3. 元素在iframe里。
4. 元素是动态生成的。
1. 在浏览器开发者工具中用 $x() $$() 验证XPath/CSS。
2. 增加显式等待 ,等元素出现或可见。
3. 检查并 switch_to.frame
4. 使用更稳定的属性或等待JS执行完毕。
ElementNotInteractableException 1. 元素被遮挡(弹窗、其他元素)。
2. 元素不可见( display:none )。
3. 元素是 disabled 状态。
1. 关闭遮挡物或使用 ActionChains 点击。
2. 等待元素可见 ( visibility_of )。
3. 检查元素属性,或等待其变为 enabled
StaleElementReferenceException 你获取到的元素引用,在操作前DOM更新了(页面刷新、AJAX更新)。 重新查找元素 。这是最常见的解决方案。在封装点击/输入方法时,可以考虑在操作前瞬间重新定位。
脚本在本地能跑,在CI服务器失败 1. CI环境无图形界面(headless)。
2. 浏览器/驱动版本不匹配。
3. 环境分辨率、时区不同。
1. 确保CI脚本配置了无头模式选项。
2. 使用 webdriver-manager 确保版本一致。
3. 在CI配置中设置统一的环境变量。

5.2 提升稳定性的进阶技巧

  1. 使用 expected_conditions 的进阶组合

    # 等待元素存在且可见且可点击,这才是最稳妥的“可操作状态”
    from selenium.webdriver.support.expected_conditions import all_of
    element = wait.until(all_of(
        EC.presence_of_element_located(locator),
        EC.visibility_of_element_located(locator),
        EC.element_to_be_clickable(locator)
    ))
    
  2. 重试机制 :对于网络波动等偶发问题,可以给关键操作加上重试。

    from tenacity import retry, stop_after_attempt, wait_fixed
    @retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
    def click_with_retry(locator):
        self.find_element(locator).click()
    
  3. 屏蔽不必要的信息 :浏览器日志、性能日志可能会干扰测试,可以关闭。

    options = webdriver.ChromeOptions()
    options.add_experimental_option('excludeSwitches', ['enable-logging']) # 关闭DevTools日志
    prefs = {"profile.default_content_setting_values.notifications": 2} # 禁用通知
    options.add_experimental_option("prefs", prefs)
    
  4. 并行测试 :当用例很多时,使用 pytest-xdist 插件可以大幅缩短执行时间。

    pip install pytest-xdist
    pytest -n 4 # 使用4个worker并行运行
    

5.3 框架的维护与扩展

  • 定期更新依赖 :定期运行 pip list --outdated 检查并更新 selenium , webdriver-manager 等库。
  • 代码审查 :对页面对象类和测试用例进行代码审查,确保定位器没有重复,业务逻辑封装得当。
  • 持续集成 :将框架接入Jenkins、GitLab CI、GitHub Actions等CI/CD工具,实现定时执行或代码提交触发。
  • 容器化 :使用Docker将测试环境(包括浏览器、驱动、Python环境)打包,确保在任何机器上运行结果一致。

构建一个自动化框架不是一蹴而就的,它需要在实际项目中不断迭代和优化。从最简单的封装开始,逐步引入PO模式、数据驱动、配置文件管理。每当遇到一个重复性的问题或痛点,就思考能否在框架层面解决它。记住,好的框架不是设计出来的,而是演化出来的。它最终会成为你和团队在质量保障道路上最得力的助手。

更多推荐