尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python+Selenium自动化测试框架:从PO模式到数据驱动的工程实践

Python+Selenium自动化测试框架:从PO模式到数据驱动的工程实践 1. 项目概述为什么需要一个PythonSelenium自动化测试框架如果你正在做Web产品的测试工作或者是一名开发想为自己的项目补充自动化测试能力那么“Selenium自动化测试框架”这个概念你一定不陌生。简单来说Selenium是一个让你能用代码控制浏览器模拟真人操作的工具比如点击按钮、输入文字、验证页面内容。但如果你只是写几个零散的脚本很快就会遇到问题脚本难以维护、用例数据混乱、报告不直观、无法团队协作。这时候一个结构化的“框架”就至关重要了。我见过很多新手包括几年前的我自己都是从写一个简单的driver.find_element_by_id(“submit”).click()开始的。这没问题但当你有几十上百个测试用例时你会发现改一个页面元素需要翻遍所有脚本加一个测试场景复制粘贴的代码到处都是。一个设计良好的自动化测试框架核心目标就是解决这些工程化问题提升脚本的可维护性、可读性和复用性同时将测试数据、业务逻辑和用例执行分离。它不是一个炫技的工具而是一个让你和你的团队能长期、高效、稳定地开展自动化测试的基础设施。基于Python和Selenium构建框架是目前最主流、生态最成熟的选择之一。Python语法简洁Selenium API强大两者结合能快速上手。但“会用Selenium”和“能搭建一个健壮的测试框架”之间隔着一道巨大的鸿沟。接下来我将结合我多年的实战和踩坑经验为你拆解一个工业级PythonSelenium自动化测试框架的核心设计、实现细节和那些文档里不会写的“坑”。2. 框架整体设计与核心思路拆解在动手写代码之前想清楚框架的设计思路比盲目开始更重要。一个好的框架设计应该像搭积木一样模块清晰职责单一方便扩展。下面这张图展示了一个典型的分层架构也是我们接下来要实现的蓝图测试框架分层架构 ├── 用例层 (TestCase Layer) ├── 业务层 (Page Object Layer) ├── 基础层 (Base Layer) └── 框架支持层 (Framework Support Layer)2.1 为什么选择Page ObjectPO模式这是自动化测试框架设计的基石。PO模式的核心思想是将页面元素定位和业务操作流程分离。每一个Web页面或页面中的一个重要组件对应一个Python类这个类里只做两件事定义这个页面上所有需要操作的元素如输入框、按钮的定位方式。封装在这个页面上可以进行的操作如登录、搜索、提交。这样做的好处是显而易见的当页面UI发生变更时比如一个按钮的ID改了你只需要去对应的Page类里修改这一个地方的定位符所有用到这个按钮的测试用例都无需改动。这极大地降低了维护成本。如果没有PO模式元素定位散落在成百上千个测试用例中一次UI改版可能就是测试团队的噩梦。2.2 数据驱动测试用YAML管理测试数据测试数据如登录用的用户名、密码和测试用例逻辑硬编码在一起是另一个常见的坏味道。数据驱动测试Data-Driven Testing将测试数据从脚本中抽离出来用外部文件如YAML、JSON、Excel来管理。我强烈推荐使用YAML因为它格式清晰可读性好同时支持复杂的数据结构非常适合描述测试场景。例如一个登录测试可能需要测试“正确密码”、“错误密码”、“空密码”等多种情况。在YAML文件里你可以这样组织- id: TC_LOGIN_01 detail: “使用正确用户名和密码登录” data: username: “standard_user” password: “secret_sauce” expected: “登录成功跳转到首页” - id: TC_LOGIN_02 detail: “使用错误密码登录” data: username: “standard_user” password: “wrong” expected: “提示密码错误”在测试脚本中你只需要编写一套通用的登录流程逻辑然后框架会自动读取YAML文件中的每一组数据去执行这个流程并验证对应的预期结果。添加新的测试场景只需在YAML文件里新增一组数据即可无需修改代码。2.3 测试执行与报告unittest/pytest HTML报告我们需要一个“发动机”来组织、运行测试用例并收集结果。Python标准库中的unittest或更强大的第三方库pytest是首选。它们提供了测试用例发现、夹具fixtures用于setup/teardown、断言等功能。光有控制台输出是不够的我们需要一份直观的HTML测试报告能清晰地展示通过了多少、失败了多少、失败的原因和截图。HTMLTestRunner是一个经典的选择虽然有些老旧但稳定可靠。pytest则有丰富的插件生态如pytest-html可以生成更现代的报表。框架需要集成报告生成功能并在测试结束后自动打开报告或通过邮件发送。2.4 目录结构设计清晰即正义一个混乱的目录结构是项目腐化的开始。我们应该在项目伊始就规划好清晰的目录。一个推荐的目录结构如下project_root/ ├── config/ # 配置文件目录 │ ├── setting.py # 全局配置浏览器类型、URL、超时时间等 │ └── element.yaml # 可选全局页面元素定位另一种管理方式 ├── test_data/ # 测试数据目录 │ └── login_data.yaml # 登录模块的测试数据 ├── page_objects/ # 页面对象目录 │ ├── base_page.py # 基础页面类封装通用操作 │ ├── login_page.py # 登录页面类 │ └── home_page.py # 首页页面类 ├── test_cases/ # 测试用例目录 │ ├── test_login.py # 登录测试用例 │ └── test_search.py # 搜索测试用例 ├── utils/ # 工具函数目录 │ ├── logger.py # 日志记录模块 │ ├── screenshot.py # 截图工具 │ └── send_email.py # 邮件发送工具 ├── reports/ # 测试报告输出目录 │ ├── html_reports/ │ └── screenshots/ # 失败用例截图 ├── drivers/ # 浏览器驱动目录如chromedriver.exe └── run_tests.py # 测试执行主入口这样的结构让任何人都能快速找到他们需要的文件也符合Python的模块化导入规范。3. 核心模块实现与实操要点理论说完了我们开始动手搭建。我会逐一拆解每个核心模块的实现并附上代码和关键解释。3.1 环境准备与基础配置首先确保你的环境已经就绪。你需要安装Python建议3.7以上版本然后使用pip安装核心库pip install selenium pip install PyYAML # 用于读写YAML文件 pip install pytest # 如果你选择pytest作为测试运行器 pip install pytest-html # 用于生成pytest-html报告关于浏览器驱动这是新手最容易踩坑的地方。Selenium WebDriver需要通过一个特定的“驱动”来与浏览器对话。以Chrome为例查看你本地Chrome浏览器的版本在浏览器地址栏输入chrome://version/。去 ChromeDriver官网 下载版本号完全匹配的驱动。将下载的chromedriver.exe(Windows) 或chromedriver(Mac/Linux) 放在项目drivers/目录下或者将其所在路径添加到系统的PATH环境变量中。注意浏览器自动更新后驱动版本可能不匹配导致脚本无法启动浏览器。一种更稳定的做法是在代码中指定驱动路径并将驱动文件纳入项目版本管理或者使用webdriver-manager库自动管理驱动版本pip install webdriver-manager。这样代码可以自动下载匹配的驱动省去手动管理的麻烦。接下来创建配置文件config/setting.py将所有的可变参数集中管理# config/setting.py import os from pathlib import Path # 项目根目录 BASE_DIR Path(__file__).resolve().parent.parent # 测试数据目录 TEST_DATA_DIR os.path.join(BASE_DIR, ‘test_data’) # 页面对象元素定位文件目录如果采用YAML管理元素 TEST_ELEMENT_DIR os.path.join(BASE_DIR, ‘test_element’) # 测试用例目录 TEST_CASE_DIR os.path.join(BASE_DIR, ‘test_cases’) # 报告输出目录 REPORT_DIR os.path.join(BASE_DIR, ‘reports’) HTML_REPORT_DIR os.path.join(REPORT_DIR, ‘html’) SCREENSHOT_DIR os.path.join(REPORT_DIR, ‘screenshots’) # 浏览器驱动路径 DRIVER_PATH os.path.join(BASE_DIR, ‘drivers’, ‘chromedriver’) # 根据系统调整扩展名 # 被测系统基础URL BASE_URL ‘https://www.your-test-site.com’ # 浏览器类型Chrome, Firefox, Edge BROWSER ‘Chrome’ # 隐式等待时间秒 IMPLICIT_WAIT 10 # 显式等待超时时间秒 EXPLICIT_WAIT_TIMEOUT 103.2 实现基础页面类这是所有页面对象类的父类封装了Selenium最常用的通用操作避免在每个页面类里重复写find_element、click等基础代码。这是框架复用性的关键。# page_objects/base_page.py from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException import logging from config.setting import IMPLICIT_WAIT, EXPLICIT_WAIT_TIMEOUT class BasePage: def __init__(self, driver): self.driver driver self.driver.implicitly_wait(IMPLICIT_WAIT) self.logger logging.getLogger(__name__) self.wait WebDriverWait(self.driver, EXPLICIT_WAIT_TIMEOUT) def find_element(self, locator): 查找单个元素加入显式等待和日志 try: self.logger.info(f”正在查找元素: {locator}”) # 使用显式等待等待元素可见并可交互 element self.wait.until(EC.visibility_of_element_located(locator)) return element except TimeoutException: self.logger.error(f”查找元素超时: {locator}”) raise # 向上抛出异常让测试用例捕获并失败 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) self.logger.info(f”向元素 {locator} 输入文本: {text}”) element.clear() element.send_keys(text) def get_text(self, locator): 获取元素的文本内容 element self.find_element(locator) text element.text self.logger.info(f”获取元素 {locator} 的文本: {text}”) return text def is_element_present(self, locator, timeout5): 判断元素是否存在不抛异常 try: WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) return True except TimeoutException: return False def take_screenshot(self, filename): 截取屏幕并保存到报告目录 import os from config.setting import SCREENSHOT_DIR if not os.path.exists(SCREENSHOT_DIR): os.makedirs(SCREENSHOT_DIR) filepath os.path.join(SCREENSHOT_DIR, f”{filename}.png”) self.driver.save_screenshot(filepath) self.logger.info(f”截图已保存至: {filepath}”) return filepath # 返回路径可用于报告链接关键点解析显式等待优于隐式等待WebDriverWait配合expected_conditions是处理动态加载元素的黄金标准。它针对特定条件进行等待更精确避免了全局隐式等待可能带来的不必要的延迟。日志记录在每个关键操作中加入日志当测试失败时通过查看日志可以快速定位到是哪一步出了问题是调试的利器。封装常用操作像input_text先执行clear()是符合大多数输入场景的。这样的封装让页面类代码更简洁意图更清晰。3.3 实现页面对象类以登录页面为例我们创建一个LoginPage类继承自BasePage。# page_objects/login_page.py from selenium.webdriver.common.by import By from page_objects.base_page import BasePage from config.setting import BASE_URL class LoginPage(BasePage): # 1. 定义页面元素定位器Locators # 这是PO模式的核心所有元素定位集中在此处 USERNAME_INPUT (By.ID, ‘username’) # 使用By类更清晰 PASSWORD_INPUT (By.ID, ‘password’) LOGIN_BUTTON (By.XPATH, ‘//button[type“submit”]’) ERROR_MESSAGE (By.CLASS_NAME, ‘error-message’) SUCCESS_INDICATOR (By.ID, ‘welcome’) # 页面URL可选用于打开页面 URL f”{BASE_URL}/login” def __init__(self, driver): super().__init__(driver) def open(self): 打开登录页面 self.logger.info(f”打开登录页面: {self.URL}”) self.driver.get(self.URL) return self # 支持链式调用 # 2. 封装页面操作行为 def enter_username(self, username): self.input_text(self.USERNAME_INPUT, username) return self def enter_password(self, password): self.input_text(self.PASSWORD_INPUT, password) return self def click_login(self): self.click(self.LOGIN_BUTTON) return self # 3. 组合业务流完整的登录操作 def login(self, username, password): 完整的登录流程 self.logger.info(f”执行登录操作用户名: {username}”) self.open() self.enter_username(username) self.enter_password(password) self.click_login() # 登录后通常需要返回下一个页面对象如首页 from page_objects.home_page import HomePage return HomePage(self.driver) # 4. 封装页面状态验证方法 def get_error_message(self): return self.get_text(self.ERROR_MESSAGE) def is_login_successful(self): return self.is_element_present(self.SUCCESS_INDICATOR)实操心得链式调用像self.enter_username(‘admin’).enter_password(‘123’).click_login()这样的写法非常流畅通过在方法中返回self即可实现。返回下一个页面对象login方法最后返回了HomePage实例。这符合实际业务流登录后进入首页使得测试用例的编写更符合自然语言home_page login_page.login(‘user’, ‘pass’)。定位器管理将所有定位器定义为类变量一目了然。如果前端元素经常变动可以考虑将定位信息进一步外置到YAML文件中通过一个读取类来动态获取实现定位信息与代码的完全分离。3.4 实现数据驱动测试用例我们使用pytest和其参数化功能来实现数据驱动。首先准备YAML测试数据文件。# test_data/login_data.yaml - test_id: “TC_LOGIN_01” description: “使用正确的管理员账号登录” username: “admin” password: “correct_password” expected_result: “success” screenshot_on: “pass” # 仅在成功时截图 - test_id: “TC_LOGIN_02” description: “使用错误的密码登录” username: “admin” password: “wrong” expected_result: “error” expected_error_msg: “用户名或密码错误” screenshot_on: “fail” # 仅在失败时截图 - test_id: “TC_LOGIN_03” description: “用户名为空登录” username: “” password: “somepassword” expected_result: “error” expected_error_msg: “用户名不能为空” screenshot_on: “always” # 总是截图然后创建一个工具函数来读取YAML数据# utils/data_loader.py import yaml import os from config.setting import TEST_DATA_DIR def load_test_data(yaml_file_name): 加载指定YAML文件中的测试数据 file_path os.path.join(TEST_DATA_DIR, yaml_file_name) with open(file_path, ‘r’, encoding‘utf-8’) as file: data yaml.safe_load(file) # 使用safe_load更安全 return data最后编写使用pytest的测试用例# test_cases/test_login.py import pytest import logging from page_objects.login_page import LoginPage from utils.data_loader import load_test_data # 获取日志器 logger logging.getLogger(__name__) # 使用pytest的fixture来管理driver的生命周期 pytest.fixture(scope“function”) # 每个测试函数一个独立的driver def driver_init(request): from selenium import webdriver from config.setting import DRIVER_PATH, BROWSER if BROWSER “Chrome”: # 使用Options可以配置浏览器行为如无头模式 options webdriver.ChromeOptions() options.add_argument(‘--ignore-certificate-errors’) # options.add_argument(‘--headless’) # 无头模式不打开GUI适合CI/CD driver webdriver.Chrome(executable_pathDRIVER_PATH, optionsoptions) elif BROWSER “Firefox”: driver webdriver.Firefox(executable_pathDRIVER_PATH) else: raise ValueError(f”不支持的浏览器类型: {BROWSER}”) driver.maximize_window() request.cls.driver driver # 将driver赋给测试类 yield driver # 测试结束后退出浏览器 driver.quit() pytest.mark.usefixtures(“driver_init”) class TestLogin: 登录功能测试集 # 参数化从YAML文件加载数据每一条数据都会生成一个独立的测试用例 pytest.mark.parametrize(“test_case”, load_test_data(“login_data.yaml”)) def test_login_with_data(self, test_case, request): 数据驱动登录测试 logger.info(f”开始执行用例: {test_case[‘test_id’]} - {test_case[‘description’]}”) # 初始化页面对象 login_page LoginPage(self.driver) # 执行登录操作 login_page.login(test_case[‘username’], test_case[‘password’]) # 根据预期结果进行断言 if test_case[‘expected_result’] “success”: # 验证登录成功 assert login_page.is_login_successful(), f”用例 {test_case[‘test_id’]} 失败登录成功后未找到成功标识” logger.info(f”用例 {test_case[‘test_id’]} 断言成功”) # 根据配置决定是否截图 if test_case.get(‘screenshot_on’) in [‘pass’, ‘always’]: login_page.take_screenshot(f”PASS_{test_case[‘test_id’]}”) elif test_case[‘expected_result’] “error”: # 验证登录失败并检查错误信息 actual_error login_page.get_error_message() expected_error test_case[‘expected_error_msg’] assert actual_error expected_error, f”用例 {test_case[‘test_id’]} 失败期望错误信息 ‘{expected_error}’实际得到 ‘{actual_error}’” logger.info(f”用例 {test_case[‘test_id’]} 断言成功错误信息符合预期”) if test_case.get(‘screenshot_on’) in [‘fail’, ‘always’]: login_page.take_screenshot(f”FAIL_{test_case[‘test_id’]}”) else: pytest.fail(f”测试用例 {test_case[‘test_id’]} 的expected_result配置错误”)关键点解析pytest.fixture这是pytest的灵魂。driver_initfixture 负责在每个测试开始前创建浏览器实例测试结束后关闭。scope“function”确保每个测试用例都在一个干净的浏览器环境中运行互不干扰。pytest.mark.parametrize这是实现数据驱动的核心装饰器。它自动将login_data.yaml中的每一条数据注入到test_case参数中从而生成多个测试用例。在测试报告中你会看到test_login_with_data[TC_LOGIN_01]test_login_with_data[TC_LOGIN_02]等独立的用例条目非常清晰。灵活的断言与截图根据YAML中expected_result和screenshot_on的配置动态决定断言逻辑和截图策略使得测试用例非常灵活。3.5 生成漂亮的HTML测试报告使用pytest-html插件可以轻松生成现代风格的HTML报告。首先安装插件然后在运行测试时添加参数。创建一个主运行文件run_tests.py# run_tests.py import pytest import os from datetime import datetime from config.setting import HTML_REPORT_DIR def main(): # 确保报告目录存在 if not os.path.exists(HTML_REPORT_DIR): os.makedirs(HTML_REPORT_DIR) # 生成带时间戳的报告文件名 current_time datetime.now().strftime(“%Y%m%d_%H%M%S”) report_file os.path.join(HTML_REPORT_DIR, f”test_report_{current_time}.html”) # pytest命令行参数 # -v: 详细输出 # -s: 允许终端输出如print语句 # --html: 生成html报告 # --self-contained-html: 将CSS等资源嵌入HTML生成单个文件 # --captureno: 等同于 -s pytest_args [ “test_cases/”, # 测试用例目录 “-v”, “-s”, f”—html{report_file}”, “—self-contained-html”, “—captureno”, ] # 运行测试 exit_code pytest.main(pytest_args) print(f”\n测试报告已生成: file://{os.path.abspath(report_file)}”) # 可以尝试用默认浏览器打开报告可选 # import webbrowser # webbrowser.open(f”file://{os.path.abspath(report_file)}”) return exit_code if __name__ “__main__”: exit(main())运行python run_tests.py测试结束后会在reports/html/目录下生成一个包含详细结果、日志、甚至截图链接的HTML文件。报告会清晰展示通过/失败/跳过的用例数量、执行时间以及每个用例的详细日志。4. 高级技巧与最佳实践框架搭起来了但要让它真正稳健、高效还需要一些“内功心法”。4.1 智能等待与元素查找策略显式等待是必须的但如何用好它不要滥用time.sleep()这是最糟糕的等待方式它固定等待指定时间无论元素是否已加载完成都会拖慢测试速度。只在极少数明确需要固定等待的场景下使用。使用自定义等待条件expected_conditions提供了很多内置条件如元素可见、可点击、存在等。你还可以自定义更复杂的条件def text_to_be_present_in_element_value(locator, text): 自定义等待条件等待元素value属性包含特定文本 def _predicate(driver): try: element_text driver.find_element(*locator).get_attribute(“value”) return text in element_text except StaleElementReferenceException: return False return _predicate # 使用 wait.until(text_to_be_present_in_element_value((By.ID, “search”), “Python”))定位器策略优先使用ID和Name因为它们通常最稳定且查找最快。其次考虑CSS Selector它比XPath更高效、可读性更好。XPath功能强大但性能相对较差且容易因DOM结构微小变动而失效谨慎使用。尽量避免使用包含索引如div[3]或绝对路径的XPath。4.2 测试夹具的深度使用pytest的 fixture 不仅可以管理 driver还能做更多会话级Fixturepytest.fixture(scope“session”)在整个测试会话中只执行一次适合初始化一些昂贵的资源如数据库连接。自动使用Fixturepytest.mark.usefixtures(“fixture_name”)可以标记类或模块使其自动应用某个fixture。Fixture 参数化Fixture 本身也可以参数化为不同的测试提供不同的初始数据。4.3 日志与失败分析完善的日志是调试的救命稻草。建议使用Python标准库logging模块并合理配置级别DEBUG, INFO, WARNING, ERROR。在框架关键节点记录INFO日志如“开始执行用例XXX”、“点击登录按钮”。在元素查找失败、断言失败时记录ERROR日志并附上上下文信息。将日志输出到文件和控制台方便实时查看和事后追溯。在HTML测试报告中集成日志pytest-html默认会捕获测试期间的日志并展示在报告中。4.4 集成到CI/CD流水线自动化测试只有集成到持续集成/持续部署流程中才能最大化其价值。使用无头模式在CI服务器如Jenkins, GitLab CI, GitHub Actions上运行时没有图形界面。需要在创建driver时添加无头模式选项options.add_argument(‘--headless’) options.add_argument(‘--no-sandbox’) # Linux环境下常需要 options.add_argument(‘--disable-dev-shm-usage’) # 解决共享内存问题稳定的环境确保CI环境中的浏览器和驱动版本与本地开发环境一致可以使用webdriver-manager或直接在CI镜像中固定版本。测试结果通知在run_tests.py中集成邮件发送功能如使用smtplib和email库当测试失败时自动将HTML报告通过邮件发送给相关责任人。测试数据隔离CI环境应使用独立的测试数据库或账号避免与本地或其他环境的数据冲突。5. 常见问题与排查技巧实录即使框架设计得再好在实际运行中也会遇到各种“坑”。这里记录了一些典型问题及其解决方案。5.1 元素找不到或状态不对这是最常见的问题没有之一。问题现象NoSuchElementException,ElementNotVisibleException,ElementNotInteractableException。排查步骤确认定位符是否正确在浏览器的开发者工具F12中使用Console尝试$x(‘你的XPath’)或$(‘你的CSS Selector’)看是否能找到元素。确认页面是否加载完成可能是等待时间不够。尝试增加显式等待的超时时间或者检查等待的条件是否正确例如等待元素“可点击”而不仅仅是“存在”。确认是否有iframe如果目标元素在iframe内必须先使用driver.switch_to.frame(frame_reference)切换到对应的iframe中才能操作其中的元素。操作完后记得用driver.switch_to.default_content()切回主文档。确认是否有新窗口/标签页点击后打开了新窗口需要先使用driver.switch_to.window(window_handle)切换到新窗口。元素被遮挡有时元素被其他元素如弹窗、遮罩层覆盖。可以尝试使用ActionChains或执行JavaScript来点击。element driver.find_element(...) driver.execute_script(“arguments[0].click();”, element)5.2 测试执行速度慢优化等待用显式等待替代固定等待和过长的隐式等待。分析哪些操作后需要等待使用最精确的等待条件。并行执行pytest支持通过pytest-xdist插件并行运行测试。可以按模块或类分发测试显著缩短总执行时间。pip install pytest-xdist pytest -n auto # 使用与CPU核心数相同的worker并行运行减少不必要的浏览器重启对于不依赖严格隔离的测试可以考虑使用scope“class”或scope“module”的fixture来复用浏览器实例。但要注意清理测试数据避免用例间相互影响。5.3 测试在CI上失败但在本地成功这是一个令人头疼的问题通常与环境差异有关。浏览器/驱动版本不匹配CI服务器上的浏览器版本可能与你本地不同。使用webdriver-manager或确保CI镜像中的版本固定。资源限制CI服务器的内存或CPU可能不足导致页面加载缓慢或超时。适当增加等待超时时间或优化CI服务器的资源配置。文件路径问题代码中使用的相对路径在CI服务器上可能不存在。始终使用绝对路径可以通过我们之前在setting.py中定义的BASE_DIR来构建。网络环境被测应用在CI环境可能访问的是不同的地址如内网地址或者网络延迟更高。检查BASE_URL的配置。5.4 动态内容与验证码处理动态ID/Class如果元素的ID或Class是每次页面加载随机生成的就不能用它们来定位。寻找其父元素或相邻元素中稳定的属性或者使用XPath的文本内容、部分属性匹配contains等方式。# 不好的定位id是动态的 # button driver.find_element(By.ID, “submit-12345-random”) # 好的定位使用稳定的文本内容 button driver.find_element(By.XPATH, “//button[text()‘提交’]”)验证码这是一个自动化测试的“终结者”。对于真正的验证码自动化测试无法也不应该去破解。有以下几种策略测试环境屏蔽验证码这是最常用的方法。与开发团队协作在测试环境中提供一个万能验证码如“0000”或者直接关闭验证码功能。使用Cookie或Token绕过通过API先获取登录态然后将Cookie或Token注入到浏览器会话中直接访问登录后的页面。使用第三方打码服务不推荐成本高稳定性差仅作为最后的手段。搭建一个PythonSelenium自动化测试框架从设计到实现再到优化和排错是一个系统工程。它考验的不仅是编码能力更是对测试流程、软件工程和问题解决的综合理解。这个框架不是一成不变的你可以根据自己项目的实际情况引入更多的组件比如API测试集成、数据库断言、性能监控等让它不断进化更好地为你的产品质量保驾护航。记住好的框架是迭代出来的先从满足核心需求开始然后在使用中不断完善。
返回列表