Selenium Web自动化测试:从核心原理到企业级框架实战
1. 项目概述为什么Selenium依然是Web自动化测试的基石如果你在软件测试或者Web开发领域待过一段时间Selenium这个名字绝对如雷贯耳。它不是一个新潮的工具但绝对是那个“老而弥坚”的基石。我见过太多团队从最初的手工点击到尝试各种花哨的新框架最后兜兜转转发现解决Web浏览器自动化最稳定、最普适的方案往往还是Selenium。说它“革命性”一点不为过因为它真正把我们从重复、枯燥的浏览器操作中解放出来让机器去模拟人的行为进行回归测试、数据抓取甚至是复杂的业务流程验证。简单来说Selenium就是一个让你用代码来控制浏览器的工具。你可以用Python、Java、C#、JavaScript等几乎所有主流编程语言编写脚本去命令浏览器打开网页、点击按钮、输入文字、下拉选择、验证页面内容。这听起来简单但背后解决的是一个巨大的痛点Web应用的界面和行为会频繁变动每次变动后的人工回归测试耗时耗力且容易遗漏。Selenium自动化脚本可以7x24小时不间断执行快速反馈问题把测试人员从重复劳动中解放出来去关注更复杂的测试场景和用户体验。那么谁需要了解Selenium呢首先是测试工程师这是他们的核心饭碗之一其次是开发工程师用于构建自验证的代码或进行简单的冒烟测试再者是爬虫工程师虽然现在反爬机制复杂但Selenium模拟真人操作依然是绕过一些检测的手段之一最后任何需要与Web界面进行规律性、批量交互的业务人员比如定期从某个内部系统导出报表也可以用Selenium小脚本实现。接下来我会结合我十多年的踩坑经验为你拆解Selenium的核心以及如何构建一个稳健、可维护的自动化测试体系。2. Selenium核心架构与生态组件深度解析很多人一上来就pip install selenium然后就开始写find_element但对它底下是怎么运作的并不清楚。这就像开车不懂发动机一旦抛锚就束手无策。要玩转Selenium必须理解它的三层架构。2.1 WebDriver浏览器与代码的“翻译官”这是Selenium的核心。你的测试脚本用Python、Java等写成并不能直接和Chrome、Firefox这些浏览器对话。WebDriver就充当了这个“翻译官”或“桥梁”。它是一个独立的可执行程序如chromedriver.exe,geckodriver.exe遵循W3C的WebDriver协议。它的工作流程是这样的你的脚本例如Python代码调用Selenium客户端库如selenium包的方法。客户端库将你的指令如“打开百度”、“搜索框输入关键词”转换为HTTP请求发送给对应浏览器的WebDriver。WebDriver接收到请求后通过浏览器提供的自动化接口如Chrome DevTools Protocol来真正操控浏览器。浏览器执行操作后将结果如元素是否找到、页面标题等通过WebDriver返回给客户端库最终反馈给你的脚本。注意WebDriver版本必须与本地安装的浏览器大版本号匹配这是新手最常见的坑。Chrome 120版本就需要chromedriver 120.x用121或119的很可能失败。务必去官方或镜像站下载匹配的版本。2.2 Selenium IDE快速入门的“录制回放”工具这是一个浏览器插件Chrome/Firefox允许你通过录制操作来生成测试脚本。你点点鼠标它记录你的操作并生成代码支持多种语言。这对于快速创建原型、学习Selenium API或者测试简单流程非常有用。但我必须强调不要依赖IDE录制生成的脚本用于生产环境录制的脚本通常脆弱不堪因为它依赖于非常具体的元素定位器如冗长的XPath页面结构稍有变动脚本就失效了。它的正确用途是1) 新手学习命令2) 快速获取某个复杂元素的可能定位方式3) 验证简单流程。真正的自动化测试框架必须手写采用稳定的定位策略和页面对象模型。2.3 Selenium Grid分布式执行的“指挥中心”当你的测试用例成百上千需要跨不同浏览器Chrome, Firefox, Edge、不同操作系统Windows, macOS, Linux或需要并行执行以缩短反馈时间时Selenium Grid就派上用场了。它采用Hub-Node架构Hub中心调度器。你的测试脚本指向Hub。Node执行节点。在安装了目标浏览器和对应WebDriver的机器上注册到Hub。当你向Hub发起一个测试请求要求“在Windows 10的Chrome 120上执行”Hub会寻找符合条件的空闲Node将测试任务分发过去执行。这对于搭建持续集成CI/CD环境中的测试集群至关重要可以实现快速的跨平台兼容性测试。2.4 客户端语言绑定你手中的“遥控器”这就是你实际写代码用的库如Python的selenium包Java的selenium-java依赖。它们提供了一套统一的API让你用熟悉的语言发送指令。选择哪种语言通常取决于团队的技术栈。Python因其简洁易学、生态丰富pytest在测试领域非常流行Java则在大型企业级项目中更常见。3. 从环境搭建到第一个脚本避开初学者的所有坑理论懂了我们动手。这里我以Python Chrome为例展示最稳妥的搭建流程。3.1 环境准备与精准配置安装Python去python.org下载安装建议版本3.8以上。安装时务必勾选“Add Python to PATH”。安装Selenium客户端库打开命令行CMD或Terminal执行pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple。使用国内镜像源速度更快。安装浏览器确保已安装Chrome浏览器。下载并配置ChromeDriver打开Chrome在地址栏输入chrome://settings/help查看版本号例如 120.0.6099.110。访问淘宝NPM镜像https://npmmirror.com/mirrors/chromedriver/或官方站点找到对应大版本号120的目录下载与你的操作系统匹配的驱动。关键一步配置驱动路径。有三种方法我推荐第三种方法一将chromedriver.exe放在Python安装目录的Scripts文件夹下该目录已在PATH中。方法二将chromedriver.exe所在目录添加到系统的PATH环境变量。方法三最推荐显式指定路径在代码中指定驱动路径。这避免了环境依赖项目移植更方便。3.2 第一个脚本不仅仅是“Hello World”我们来写一个访问百度并搜索的脚本。这个简单的脚本里藏着好几个最佳实践。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time # 1. 初始化驱动显式指定路径 driver_path r你的路径\chromedriver.exe # 例如C:\tools\chromedriver.exe service webdriver.ChromeService(executable_pathdriver_path) # 注意新版本API driver webdriver.Chrome(serviceservice) # 2. 设置隐式等待全局等待元素出现的超时时间 driver.implicitly_wait(10) # 单位秒 try: # 3. 打开百度 driver.get(https://www.baidu.com) print(f当前页面标题是{driver.title}) # 4. 定位搜索框并输入关键词 # 使用ID定位这是最优先的策略 search_box driver.find_element(By.ID, kw) search_box.clear() # 良好的习惯先清空输入框 search_box.send_keys(Selenium自动化测试) # 5. 定位搜索按钮并点击 search_button driver.find_element(By.ID, su) search_button.click() # 6. 使用显式等待等待搜索结果区域出现 # 这是比time.sleep()和隐式等待更精准、高效的方式 wait WebDriverWait(driver, 10) result_stats wait.until( EC.presence_of_element_located((By.ID, content_left)) ) print(搜索结果页面加载完成。) # 7. 验证搜索结果简单示例检查页面标题或特定结果 assert Selenium自动化测试 in driver.title print(标题验证通过。) # 8. 获取第一页所有搜索结果的标题示例 # 注意百度搜索结果结构可能变化此定位器可能需要调整 result_titles driver.find_elements(By.CSS_SELECTOR, h3.c-title a) for i, title in enumerate(result_titles[:5]): # 打印前5个 print(f结果{i1}: {title.text}) time.sleep(3) # 为了演示等待3秒看结果 except Exception as e: print(f执行过程中发生错误{e}) # 这里可以加入截图功能便于排查问题 driver.save_screenshot(error_screenshot.png) finally: # 9. 无论如何最后都要关闭浏览器 driver.quit() print(浏览器已关闭。)脚本解读与避坑指南驱动初始化新版本Selenium4.6推荐使用webdriver.ChromeService来指定路径更清晰。implicitly_wait设置一个全局的“寻找元素”超时时间。在10秒内如果find_element没立刻找到元素会轮询等待直到找到或超时。这比写死的time.sleep(5)要好避免了不必要的等待。By的使用永远使用By.IDBy.CSS_SELECTOR等而不是已废弃的find_element_by_id()。这是现代写法。WebDriverWait这是核心技巧对于需要等待特定条件如元素可见、可点击、页面加载完成的场景显式等待是唯一正确的选择。它比隐式等待更精准不会浪费不必要的等待时间。EC.presence_of_element_located是等待元素出现在DOM中不一定可见。如果需要点击应该用EC.element_to_be_clickable。assert验证自动化测试一定要有“断言”用来验证结果是否符合预期。没有断言的脚本只是“自动操作”不是“自动测试”。异常处理与截图在try...except中包裹核心操作并在出错时截图。这是定位线上问题的救命稻草。截图文件名最好加上时间戳。driver.quit()务必在finally块中调用quit()而不是close()。quit()会关闭整个浏览器进程和驱动进程释放资源close()只关闭当前标签页。4. 元素定位自动化脚本稳定性的命门超过70%的自动化脚本失败源于元素定位失效。页面改了个class名加了个div你的脚本就挂了。因此定位策略的优先级和稳定性至关重要。4.1 定位器优先级从高到低IDBy.ID。如果元素有唯一且不变的ID这是最佳选择。速度快最稳定。NameBy.NAME。对于表单元素name属性通常也比较稳定。CSS SelectorBy.CSS_SELECTOR。功能强大语法简洁性能优于XPath。优先考虑。示例#kw(ID为kw),.s_ipt(class包含s_ipt),input[namewd](name为wd的input标签)。XPathBy.XPATH。功能最强大可以遍历整个DOM树但性能稍差且容易因页面结构微调而失效。慎用绝对路径以/开头尽量用相对路径和属性结合。好的XPath//input[idkw]或//form[idform]//input[namewd]脆弱的XPath/html/body/div[3]/div[2]/div/div[1]/div/form/span[1]/inputLink Text / Partial Link TextBy.LINK_TEXT,By.PARTIAL_LINK_TEXT。仅用于超链接定位。Class NameBy.CLASS_NAME。因为CSS类名经常变动或复用稳定性较低。Tag NameBy.TAG_NAME。最不具体通常需要结合其他条件使用。4.2 高级定位与等待策略面对动态加载、iframe、Shadow DOM等复杂情况需要组合拳。处理动态ID/Class如果元素的ID是类似“button-1234-random”的动态生成值不要用它定位。寻找其父元素或兄弟元素中稳定的属性然后用CSS或XPath向下定位。# 假设有一个稳定容器div其id是‘toolbar’里面的按钮是动态id stable_button driver.find_element(By.CSS_SELECTOR, #toolbar button[typesubmit])处理iframe如果元素在iframe内部必须先切换到该iframe操作完毕后再切回。# 通过ID、Name或索引切换 driver.switch_to.frame(iframe_id) # 在iframe内操作元素 driver.find_element(By.ID, inner_element).click() # 切回主文档 driver.switch_to.default_content()处理下拉选择框Select不要用click模拟使用专门的Select类。from selenium.webdriver.support.ui import Select select_element driver.find_element(By.ID, dropdown) select Select(select_element) select.select_by_value(option_value) # 通过value选择 select.select_by_visible_text(显示文本) # 通过文本选择处理弹窗/Alertalert driver.switch_to.alert print(alert.text) # 获取弹窗文本 alert.accept() # 点击确定 # alert.dismiss() # 点击取消5. 构建企业级自动化测试框架写几个脚本不难难的是管理成百上千的测试用例让它们稳定、可维护、可报告。这就需要引入测试框架和设计模式。5.1 测试框架集成Pytest vs UnittestunittestPython标准库模仿Java的JUnit。需要写类继承TestCase方法名以test_开头。报告功能较弱。pytest目前社区事实上的标准。更灵活可以用简单的函数也可以用类。插件生态极其丰富如pytest-html生成报告pytest-xdist并行执行pytest-rerunfailures失败重试。语法更简洁。一个典型的Pytest Selenium项目结构your_project/ ├── conftest.py # Pytest fixture配置如初始化driver ├── requirements.txt # 项目依赖 ├── page_objects/ # 页面对象模型目录 │ ├── __init__.py │ ├── base_page.py # 基础页面类 │ ├── login_page.py # 登录页面类 │ └── home_page.py # 主页类 ├── tests/ # 测试用例目录 │ ├── __init__.py │ ├── test_login.py # 登录测试 │ └── test_search.py # 搜索测试 ├── utils/ # 工具类目录 │ ├── __init__.py │ ├── config_reader.py # 读取配置文件 │ └── logger.py # 日志工具 └── reports/ # 测试报告输出目录自动生成5.2 页面对象模型Page Object Model, POM这是提升自动化脚本可维护性的最关键设计模式。核心思想是将每个页面封装成一个类页面的元素定位和操作作为这个类的方法。测试用例只调用这些方法不直接包含定位器。base_page.py(基础页面类)from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def find_element(self, by, locator): 查找单个元素加入显式等待 return self.wait.until(EC.presence_of_element_located((by, locator))) def click(self, by, locator): 点击元素等待其可点击 element self.wait.until(EC.element_to_be_clickable((by, locator))) element.click() def input_text(self, by, locator, text): 输入文本先清空 element self.find_element(by, locator) element.clear() element.send_keys(text)login_page.py(登录页面类)from selenium.webdriver.common.by import By from .base_page import BasePage class LoginPage(BasePage): # 定位器Locators集中管理 USERNAME_INPUT (By.ID, username) PASSWORD_INPUT (By.ID, password) LOGIN_BUTTON (By.ID, submit) ERROR_MSG (By.CLASS_NAME, error-message) def __init__(self, driver): super().__init__(driver) self.driver driver def enter_username(self, username): self.input_text(*self.USERNAME_INPUT, username) # 元组解包 def enter_password(self, password): self.input_text(*self.PASSWORD_INPUT, password) def click_login(self): self.click(*self.LOGIN_BUTTON) def get_error_message(self): 获取错误提示文本 try: return self.find_element(*self.ERROR_MSG).text except: return None # 如果没有错误信息返回None def login(self, username, password): 完整的登录业务流 self.enter_username(username) self.enter_password(password) self.click_login()test_login.py(测试用例)import pytest from page_objects.login_page import LoginPage class TestLogin: pytest.fixture(autouseTrue) def setup(self, driver): # driver来自conftest.py中的fixture self.login_page LoginPage(driver) self.driver driver self.driver.get(https://example.com/login) def test_login_success(self): 测试登录成功 self.login_page.login(valid_user, valid_pass) # 断言登录后跳转到首页URL或页面标题变化 assert dashboard in self.driver.current_url # 或者断言欢迎信息出现 # assert Welcome in self.driver.page_source def test_login_failure_with_wrong_password(self): 测试密码错误 self.login_page.login(valid_user, wrong_pass) error_text self.login_page.get_error_message() assert error_text is not None assert 密码错误 in error_text or invalid in error_text.lower()POM的优势高可维护性页面元素定位器只存在于Page类中。页面UI变动时只需修改对应的Page类所有测试用例无需改动。高可读性测试用例读起来像自然语言login_page.login(...)业务逻辑清晰。低冗余公共操作如等待、点击封装在基类避免重复代码。5.3 数据驱动测试将测试数据用户名、密码、搜索词与测试逻辑分离。使用pytest.mark.parametrize装饰器或从外部文件JSON, Excel, CSV读取数据。import pytest test_data [ (admin, admin123, True), (admin, wrong, False), (, admin123, False), ] pytest.mark.parametrize(username, password, expected_success, test_data) def test_login_with_data(username, password, expected_success, driver): login_page LoginPage(driver) driver.get(https://example.com/login) login_page.login(username, password) if expected_success: assert dashboard in driver.current_url else: assert login_page.get_error_message() is not None5.4 测试报告与日志没有报告和日志的自动化测试是没有灵魂的。使用pytest-html插件生成美观的HTML报告并结合Python内置的logging模块记录详细执行过程。conftest.py配置示例import pytest import logging from datetime import datetime from selenium import webdriver def pytest_configure(config): Pytest配置钩子用于设置日志和报告 # 创建日志目录 log_dir logs os.makedirs(log_dir, exist_okTrue) timestamp datetime.now().strftime(%Y%m%d_%H%M%S) log_file os.path.join(log_dir, ftest_run_{timestamp}.log) # 配置logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(log_file), logging.StreamHandler() # 同时输出到控制台 ] ) pytest.fixture(scopefunction) def driver(request): 为每个测试函数提供独立的driver实例 options webdriver.ChromeOptions() options.add_argument(--headless) # 无头模式适合CI环境 options.add_argument(--no-sandbox) options.add_argument(--disable-dev-shm-usage) options.add_argument(--disable-gpu) options.add_argument(--window-size1920,1080) _driver webdriver.Chrome(optionsoptions) _driver.implicitly_wait(10) logging.info(Chrome浏览器驱动已启动。) yield _driver # 将driver提供给测试用例使用 # 测试结束后清理 if request.node.rep_call.failed: # 如果测试失败截图并保存 screenshot_name fscreenshots/failure_{request.node.name}_{datetime.now().strftime(%Y%m%d_%H%M%S)}.png _driver.save_screenshot(screenshot_name) logging.error(f测试失败截图已保存至{screenshot_name}) _driver.quit() logging.info(浏览器驱动已关闭。) pytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): 获取测试结果钩子用于失败时截图 outcome yield rep outcome.get_result() setattr(item, rep_ rep.when, rep)运行测试并生成HTML报告pytest tests/ --htmlreports/report.html --self-contained-html6. 高级技巧与实战避坑指南掌握了基础框架下面这些实战中提炼出的技巧能让你少走很多弯路。6.1 处理顽固的“元素不可点击”异常有时明明元素可见但click()就是报错ElementClickInterceptedException。常见原因和解决方案被其他元素遮挡比如弹窗、浮动广告、div层。解决方案等待遮挡物消失如果有关闭按钮。使用JavaScript直接点击driver.execute_script(arguments[0].click();, element)。这是终极武器但慎用因为它绕过了浏览器的正常交互模拟。尝试点击元素的父级或子级。元素尚未处于可交互状态虽然出现在DOM里但可能CSS属性pointer-events: none或disabled。必须用EC.element_to_be_clickable等待。页面滚动导致元素不在视口先滚动到元素位置。driver.execute_script(arguments[0].scrollIntoView(true);, element) element.click()6.2 应对动态内容与异步加载现代Web应用大量使用Ajax和前端框架数据是异步加载的。等待页面“加载完成”driver.get后不代表你要操作的数据已经渲染出来。等待特定元素出现/消失使用WebDriverWait配合EC。# 等待“加载中”的旋转图标消失 wait.until(EC.invisibility_of_element_located((By.ID, loading-spinner))) # 等待表格的第一行数据出现 wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, table tbody tr)))等待某个条件成立可以自定义等待条件。# 等待列表项数量大于5 def list_has_more_than_five_items(driver): items driver.find_elements(By.CSS_SELECTOR, .item-list li) return len(items) 5 wait.until(list_has_more_than_five_items)6.3 文件上传与下载文件上传对于input typefile元素直接使用send_keys()传入文件绝对路径即可。不要尝试模拟点击“浏览”按钮的复杂操作。upload_element driver.find_element(By.ID, file-upload) upload_element.send_keys(rC:\Users\YourName\Desktop\test_file.jpg)文件下载需要配置浏览器选项指定下载路径并禁用下载弹窗。from selenium import webdriver options webdriver.ChromeOptions() prefs { download.default_directory: rC:\Downloads, # 指定下载目录 download.prompt_for_download: False, # 禁止下载弹窗 download.directory_upgrade: True, safebrowsing.enabled: True } options.add_experimental_option(prefs, prefs) driver webdriver.Chrome(optionsoptions) # 点击下载链接后文件会自动保存到指定目录6.4 绕过反爬虫机制一些网站会检测Selenium的特征如window.navigator.webdriver属性。虽然这更多是爬虫领域的课题但测试中也可能遇到。使用undetected-chromedriver这是一个修改过的ChromeDriver能更好地隐藏自动化特征。但它不是官方方案稳定性需自行评估。添加实验性选项在旧版本可能有效但现在很多网站能识别。options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) options.add_argument(--disable-blink-featuresAutomationControlled)执行CDP命令通过Chrome DevTools Protocol覆盖webdriver属性。driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); })重要提示对于测试而言首要目标是保证功能验证而非对抗检测。如果被测网站有反自动化机制应与开发团队沟通为测试环境提供白名单或禁用相关检测。6.5 在CI/CD流水线中集成自动化测试的价值在持续集成中最大化。以Jenkins为例关键步骤环境准备在Jenkins Agent上安装Python、Chrome或使用Docker镜像包含这些。依赖安装在构建步骤中执行pip install -r requirements.txt。执行测试执行命令如pytest tests/ --htmlreport.html --self-contained-html --junitxmlresults.xml。--junitxml生成Jenkins可解析的测试结果格式。收集报告与制品使用publishHTML插件发布HTML报告使用JUnit插件解析results.xml展示测试趋势。失败处理配置失败时发送邮件/钉钉通知并附上失败截图和日志。Docker化运行是更优雅的方案可以确保环境一致性。创建一个包含所有依赖Python, Chrome, ChromeDriver, 你的代码的Docker镜像在Jenkins中直接运行这个容器执行测试。7. 常见问题排查与性能优化即使框架完善脚本还是会出错。快速定位问题是必备技能。7.1 问题排查速查表问题现象可能原因排查步骤与解决方案NoSuchElementException1. 定位器写错或元素不存在。2. 页面未加载完成。3. 元素在iframe或Shadow DOM内。4. 元素是动态生成的需要等待。1. 在浏览器开发者工具中验证定位器。2. 添加显式等待 (WebDriverWait)。3. 检查并切换到正确的iframe。4. 增加等待时间或等待特定条件。ElementNotInteractableException1. 元素被遮挡。2. 元素不可见 (display: none或visibility: hidden)。3. 元素未处于可交互状态 (disabled)。1. 移除遮挡物或使用JS点击。2. 等待元素可见 (EC.visibility_of)。3. 检查元素属性等待其变为可用。TimeoutException1. 等待条件在超时时间内未满足。2. 网络慢或页面响应慢。3. 条件逻辑有误。1. 适当增加超时时间。2. 优化等待条件使其更精确。3. 在超时前手动截图分析页面状态。脚本执行慢1. 使用了time.sleep()。2. 隐式等待时间设置过长。3. 定位器效率低如复杂XPath。4. 网络或应用本身慢。1.用显式等待替代所有固定等待。2. 合理设置隐式等待如5-10秒。3. 优化定位器优先用ID和CSS。4. 分析网络请求确认是否为应用性能问题。ChromeDriver版本不匹配Chrome浏览器升级后未更新对应版本的ChromeDriver。检查Chrome版本下载完全相同大版本号的ChromeDriver。浏览器崩溃或无响应1. 内存泄漏未正确调用driver.quit()。2. 页面资源消耗过大。3. WebDriver bug。1. 确保driver.quit()在finally块中执行。2. 尝试增加浏览器内存参数或无头模式运行。3. 降级或升级WebDriver版本。7.2 性能优化建议使用无头模式Headless在CI/CD或不需要观察UI的测试中添加--headlessnew参数。能显著减少资源消耗加快执行速度。复用浏览器会话对于需要登录的测试套件可以使用pytest的scopesession级别的fixture来初始化一次driver所有测试用例共用避免重复登录。但要注意测试之间的状态隔离。并行测试使用pytest-xdist插件可以并行运行多个测试用例充分利用多核CPU。pytest -n auto tests/。禁用不必要的浏览器功能如图片加载、CSS、JavaScript谨慎使用可能影响功能。prefs {profile.managed_default_content_settings.images: 2} # 禁用图片 options.add_experimental_option(prefs, prefs)优化定位器避免使用//开头的复杂XPath遍历整个文档。尽量使用靠近目标的ID或CSS选择器。7.3 关于“下一代”工具如Playwright, Cypress的思考近年来Playwright、Cypress等新工具确实很火它们在某些方面如自动等待、录制、跨浏览器支持提供了更好的开发者体验。但Selenium的核心优势在于语言无关性支持几乎所有主流编程语言适合已有固定技术栈的团队。协议标准基于W3C标准浏览器厂商原生支持长期稳定性高。生态成熟拥有海量的资料、社区解答和第三方库。Grid分布式测试方案非常成熟。我的建议是对于新的绿色项目可以评估Playwright它设计更现代。但对于已有大量Selenium资产或需要多语言支持的团队继续深耕Selenium并引入POM、Pytest等最佳实践依然是性价比最高、最稳妥的选择。工具只是手段构建一个稳定、可维护、易扩展的自动化测试框架的思想才是核心。掌握了Selenium这套体系再学习其他工具也会触类旁通。

相关新闻

Java 多线程并发

Java 多线程并发

Java 多线程并发 Java 并发知识库Java 线程实现 和 创建方式 继承 Tread 类 Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方 法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动…

2026/7/1 4:57:21阅读更多 →
Kiran图标主题配置脚本解析:configure脚本的工作原理与自定义选项

Kiran图标主题配置脚本解析:configure脚本的工作原理与自定义选项

Kiran图标主题配置脚本解析:configure脚本的工作原理与自定义选项 【免费下载链接】kiran-icon-theme The kiran-icon-theme package contains the standard icon theme for the Kiran desktop, which provides default appearance for icons. 项目地址: https://…

2026/7/1 4:57:21阅读更多 →
【电赛/毕设高端局】DMA数据全是0?STM32H7/F7 Cache一致性灾难、DWT纳秒测速与 CMSIS-DSP 极限榨汁指南

【电赛/毕设高端局】DMA数据全是0?STM32H7/F7 Cache一致性灾难、DWT纳秒测速与 CMSIS-DSP 极限榨汁指南

前言 现在已经是 2026 年,电赛的算力军备竞赛早已进入白热化。为了跑更复杂的控制算法和信号处理,大家纷纷抛弃了 F1/F4,拿起了主频高达 400MHz 的 STM32H7 甚至更强的双核芯片。 但无数队伍在换上 H7 后的第一天就崩溃了: “为什…

2026/7/1 4:57:21阅读更多 →
哑铃图:数据对比的优雅之选合集 - 数据可视化(66)

哑铃图:数据对比的优雅之选合集 - 数据可视化(66)

哑铃图是什么?哑铃图(Dumbbell Plot),有时也称为DNA图或杠铃图,是一种用于比较两个相关数据点的可视化图表。它源于人们对更有效数据比较方式的持续探索。在传统的时间序列比较中,我们通常使用两条折线&…

2026/7/1 6:07:25阅读更多 →
SAP PS模块实战:手把手教你用BAPI批量创建WBS和项目(附透明表查询技巧)

SAP PS模块实战:手把手教你用BAPI批量创建WBS和项目(附透明表查询技巧)

SAP PS模块实战:BAPI批量创建WBS与透明表查询全攻略在装备制造和能源工程这类大型项目中,手动逐个创建WBS元素就像用勺子挖运河——理论上可行,但实操中会让你怀疑人生。作为经历过数十个SAP PS模块实施的顾问,我总结出一套高效可…

2026/7/1 6:07:25阅读更多 →
北京到长白山没有直达高铁

北京到长白山没有直达高铁

先交代一下背景。我们3组家庭,都是北京海淀的,孩子从6岁到10岁不等,平时在同一个小区里玩,暑假想一起带娃出去走走。商量来商量去,最后选定了长白山哈尔滨——长白山夏季均温22℃,哈尔滨俄式风情拉满&#…

2026/7/1 6:07:25阅读更多 →
如何为小米穿戴设备创建个性化表盘:Mi-Create完整指南与实战教程

如何为小米穿戴设备创建个性化表盘:Mi-Create完整指南与实战教程

如何为小米穿戴设备创建个性化表盘:Mi-Create完整指南与实战教程 【免费下载链接】Mi-Create Unofficial watchface creator for Xiaomi wearables ~2021 and above 项目地址: https://gitcode.com/gh_mirrors/mi/Mi-Create 你是否厌倦了小米手环或智能手表上…

2026/7/1 6:07:25阅读更多 →
告别编译地狱!手把手教你用预编译的OSG库在Windows上快速搭建三维地球(C++/OpenGL)

告别编译地狱!手把手教你用预编译的OSG库在Windows上快速搭建三维地球(C++/OpenGL)

30分钟极速搭建三维地球:Windows平台OSG预编译库实战指南当C开发者初次接触三维地理可视化时,往往会被OpenSceneGraph(OSG)的编译依赖吓退。本文将带你绕过源码编译的深坑,直接使用预编译库在Visual Studio中快速构建可…

2026/7/1 6:07:25阅读更多 →
别再手动画图了!用Python脚本一键生成YOLO results.txt的PR曲线对比图(附完整代码)

别再手动画图了!用Python脚本一键生成YOLO results.txt的PR曲线对比图(附完整代码)

别再手动画图了!用Python脚本一键生成YOLO results.txt的PR曲线对比图(附完整代码)在目标检测领域的研究和工程实践中,PR曲线(Precision-Recall Curve)是评估模型性能的核心指标之一。无论是撰写学术论文还…

2026/7/1 6:02:25阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/7/1 4:42:14阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/7/1 5:19:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →