先交代一个背景我最早开始碰自动化测试的时候用的还是Selenium 1时代的Remote Control那会儿跑一条用例能折腾一整天定位元素全靠XPath硬写跑挂了还得靠经验猜是超时还是脚本写得不对。后来Selenium WebDriver一出来感觉整个人都解放了——同样是写自动化思路完全不一样了。这篇文章想聊的就是WebDriver体系下两个最典型、也是我日常工作里用得最多的浏览器驱动ChromeDriver和IEDriverServer。为什么单独把这两个拎出来讲因为它们的“性格”完全相反。ChromeDriver现在是整个自动驾驶测试生态里最活跃、迭代最快的驱动新功能几乎总是先支持Chromium内核这头而IE驱动则是一个“历史包袱”极重的存在很多公司因为老系统的原因还必须跑着IE 11于是你不得不同时和这两种驱动打交道。再加上这俩驱动的配置方式、踩坑点、排查思路都有各自鲜明的特征搞懂了它们再去看Edge、Firefox基本就是举一反三的事。这篇内容适合谁入门WebDriver但还没被ChromeDriver版本兼容问题“毒打”过的同学以及那些被公司老系统绑在IE 11上、每天挣扎在元素定位和浏览器弹窗里的测试开发工程师。我会把配置细节、参数含义、常见报错和排查思路都拆开讲尽量让你照着就能落地。1. 上手前的认知准备WebDriver到底在干嘛1.1 理解WebDriver协议才能理解后面的所有报错很多人一开始接触Selenium第一件事就是写代码from selenium import webdriver driver webdriver.Chrome() driver.get(https://example.com)代码跑通了还行一旦飘红就懵了——什么SessionNotCreatedException、WebDriverException、ElementNotInteractableException全是英文看着就头大。你要是只知道“这么写能跑”遇到报错就不知道怎么排查。所以我建议你先花十分钟搞清楚一个基本问题WebDriver到底是怎么把命令变成浏览器动作的。WebDriver的本质是一个基于HTTP的JSON Wire Protocol现在新版叫W3C WebDriver Protocol。你的测试脚本比如Python里的selenium库其实是一个客户端它把driver.get(url)这样的命令打包成HTTP请求发给一个监听在本地端口的服务。这个服务是谁就是ChromeDriver或IEDriverServer。服务收到请求之后再通过浏览器内部的原生自动化接口去操作真正的浏览器窗口、标签页、DOM树。浏览器执行完动作再沿原路返回结果。这个结构决定了三件事第一你的脚本和驱动之间是“进程级”的通信所以驱动进程被杀掉、端口被占用、驱动版本不对都会导致脚本直接报错第二浏览器崩溃或者没启动成功驱动是感知不到的它只会告诉你“我请求超时了”或者“session失效了”第三为什么每个浏览器都要有个独立的驱动因为每个浏览器的内部接口都不一样Chrome暴露的是DevTools协议IE走的是IE自身的COM接口你不可能让一个驱动同时指挥两种浏览器。理解了这一点你就能明白为什么后文要反复强调“驱动版本要和浏览器版本匹配”因为驱动本质上是一个适配层浏览器升级了内部协议老驱动自然就没法对话了。1.2 为什么每个浏览器都要单独装一个“驱动”这是新手问得最多的问题之一我都装了Selenium了为什么还要另外下载ChromeDriver你想象一个场景你要同时操作一台Windows电脑上的Chrome和IE跑一套跨浏览器兼容性测试。如果Selenium直接把所有浏览器操作都塞进一个库里那就意味着Selenium得维护所有浏览器的所有版本的内部接口这个工程量大到不现实。所以浏览器厂商自己维护驱动项目——谷歌做ChromeDriver微软做IEDriverServerMozilla做geckodriver。Selenium只是提供一个统一的上层API底层谁去执行交给各个驱动。这其实是个很聪明的分权策略。但副作用就是驱动版本必须跟着浏览器版本走而且Windows、Linux、macOS要分别下载对应平台的二进制。Chrome 115之前你还能指望Selenium Manager帮你省点事但老项目里这种自动管理没有普及很多人还在用硬编码的方式配置driver路径。我现在的操作习惯是不管用什么浏览器都先确认浏览器版本和驱动版本然后再决定是写死路径还是用自动管理。你如果跳到后面的章节会发现版本匹配这块我反复提了三次因为它在所有Selenium实战问题里占比奇高、报错最莫名其妙、新手最容易翻车。2. 环境搭建ChromeDriver与IEDriverServer的配置细节2.1 ChromeDriver版本匹配不是越新越好先说ChromeDriver。它和Chrome浏览器的版本对应关系有个规律性的说法ChromeDriver的大版本号要和Chrome的大版本号一致。比如Chrome 120就找ChromeDriver 120.x.x.x小版本可以略有差异但大版本必须对上。这个规则在Chrome 115之后又有点变化。Google开始推行Chrome for TestingCfT这个概念把“稳定版浏览器”和“专门用于自动化的测试浏览器”分开。所以你会发现Chrome 115之后ChromeDriver官网给出的下载列表不仅有版本号还有对应的CfT下载链接。你要是用普通Chrome配ChromeDriver大概率能跑但偶尔会遇到“session created but the browser didn’t respond”这类让人摸不着头脑的问题。此时换用CfT版本反而稳定得多。放一个我整理的对应关系表方便你自查Chrome版本ChromeDriver版本建议备注Chrome 120ChromeDriver 120.x匹配即可Chrome 115ChromeDriver 115.x开始推荐使用CfTChrome 114及以下ChromeDriver 114.x旧规则大版本一样就基本能用Chrome Dev/Beta对应版本最接近的Stable驱动不稳定测试环境慎用下载驱动之后Python这边有三种常见用法# 方式1把驱动放进系统PATH driver webdriver.Chrome() # 方式2显式指定驱动路径 driver webdriver.Chrome(executable_path/path/to/chromedriver) # 方式3用Service类Selenium 4推荐 from selenium.webdriver.chrome.service import Service service Service(executable_path/path/to/chromedriver) driver webdriver.Chrome(serviceservice)注意Selenium 4之后executable_path参数在webdriver.Chrome()里被标记为废弃了更规范的写法是用Service对象。但很多老教程还在用老写法代码也能跑就是控制台会打一行DeprecationWarning。不建议忽略这类警告因为下一个大版本可能就真的移除了。新手最容易犯的错是把驱动放在桌面或随便一个没访问权限的目录里然后脚本启动时报权限错误。Windows下尤其容易出现“不允许访问”这类问题解法很简单把驱动放到一个单独的、无空格的路径下比如D:\tools\drivers\。2.2 IEDriverServer的两大配置坑IE没有像Chrome那样清爽的跨平台驱动IEDriverServer只有Windows版本。这一点倒不意外因为IE本身就跑在Windows上。但它的配置坑比Chrome多得多而且每个坑都有“历史成因”。第一个坑是位宽匹配。IEDriverServer有32位和64位之分但这里有个反直觉的现象64位的Windows系统上64位IE驱动反而可能在定位大页面时出现一些怪异行为。社区里普遍推荐在64位系统上使用32位的IEDriverServer因为IE的原生自动化实现是基于32位COM的用32位驱动更稳定。我自己测过几轮确实是这样64位驱动偶尔会出现“定位到元素但点击无效”的问题换成32位就好了。第二个坑是“增强保护模式”。IE 11默认开启了增强保护模式这个模式会阻止IEDriverServer通过原生接口访问浏览器。你如果直接跑脚本大概率报错说“Protected Mode settings are not the same for all zones”或者干脆连session建立都失败。最简单的处理在IE的Internet选项 - 安全标签页里把“启用保护模式”的勾选状态调整到一致要么全选上、要么全不选。别笑这个“全选或全不选”的设定就是IE的怪脾气之一。W3C WebDriver规范要求所有区域的安全设置一致但IE把“是否启用保护模式”当成了区域安全设置的一部分。你如果有跨域访问的场景比如登录页是HTTPS、资源走HTTP四个区域的安全级别不一样IE就会翻脸。2.3 Selenium Manager现代项目的省心方案Selenium 4.6之后官方引入了Selenium Manager它最大的作用是自动帮你处理驱动的下载和版本匹配。你在没有设置任何webdriver.chrome.driver的情况下调用webdriver.Chrome()Selenium Manager会自己去查Chrome版本、下载对应的ChromeDriver、启动驱动进程。这个功能出来后我一度觉得写死路径的方式可以退休了。但用了一阵子发现Selenium Manager在联网正常、公司网络没有源封锁的情况下确实很爽可在内网离线环境里就直接栽了。所以我的建议是个人开发、本地调试尽管用Selenium Manager省心CI/CD流水线、内网测试环境还是显式管理驱动版本最靠谱避免网络抖动导致构建失败混合场景本地开发用自动管理跑CI时用固定版本驱动塞进镜像。这个策略说白了就是自动管理省心但不可控手动管理可控但费手。两者没有绝对好坏关键是看你的执行环境允许多大程度的“自动化”。3. Chrome自动化Options参数是最值得花时间的部分3.1 常用ChromeOptions参数逐个过一遍ChromeDriver支持非常丰富的启动参数这些参数基本对应Chrome命令行开关。你可以在启动浏览器之前定制一堆行为比如禁用GPU、关闭沙箱、设置窗口大小、伪装移动设备、关闭弹窗、忽略证书错误等等。给你看一段带常用参数的示例from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() options.add_argument(--headlessnew) # 新版无头模式 options.add_argument(--no-sandbox) # 在root用户/容器环境必加 options.add_argument(--disable-gpu) # 老参数现在不是必须但无害 options.add_argument(--disable-dev-shm-usage) # 解决/dev/shm太小导致的崩溃 options.add_argument(--window-size1920,1080) # 设置窗口大小 options.add_argument(--ignore-certificate-errors) # 跳过证书错误 options.add_argument(--disable-notifications) # 禁用通知弹窗 options.add_argument(--langzh-CN) # 设置浏览器语言 driver webdriver.Chrome(optionsoptions)这里每个参数我都遇到过对应的实际问题不是随便堆上去的--no-sandbox在Linux容器里跑测试时如果不用这个参数Chrome经常启动即崩溃。原因是Chrome默认的沙箱需要特权权限而大多数容器环境没有配置。加上它也许会在安全性上打折扣但你是在可控的测试环境跑问题不大。--disable-dev-shm-usage这是容器环境下Chrome崩溃的又一高发原因。默认的/dev/shm只有64MBChrome多标签一开就爆了。改成使用/tmp之后空间宽裕了崩溃率能降下来不少。--disable-gpu在无显示器环境里老版Chrome不开GPU反而稳定。现在新版本里这个参数越来越不重要但加上它不会有什么副作用我就保留着。--ignore-certificate-errors测试环境里自签名证书太常见了不加这个HTTPS站点直接白屏。注意生产环境千万别用。--disable-notifications防止浏览器原生的通知弹窗挡住元素。这类弹窗特别讨厌它不是页面内的元素Selenium默认无法直接关掉。--window-size这个要重点说很多前端页面没有做响应式适配窗口一变小菜单就折叠。你如果固定了窗口大小定位元素的稳定性会高很多。3.2 headless模式实战要点Headless模式是自动化测试里最常用的运行方式因为不需要真的弹出浏览器窗口可以跑在服务器上。但真到了实操很多同学发现同样的脚本带界面的能跑headless就挂了。原因集中在三点第一headless模式下浏览器窗口默认是800x600。你的脚本如果升级到1920x1080但忘了设置窗口大小页面渲染出来的布局就和预期不一样可能某些元素只在宽屏下可见。所以headless模式下务必显式设置--window-size1920,1080。第二headless模式默认的User-Agent里带了“HeadlessChrome”字样。有些网站会针对这个特征返回不同的页面甚至直接拒绝访问。解决办法是覆盖User-Agentoptions.add_argument(--user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Kill all human-readable UA strings)注意这里只是举个例子真实环境里你需要从浏览器里复制一份完整的UA字符串而不是手写一个简版。第三headless模式下浏览器的下载行为会被当成默认下载不会触发另存为对话框。如果你测试的是下载功能得配置prefs来指定下载目录和关闭自动打开PDFprefs { download.default_directory: /tmp/downloads, plugins.always_open_pdf_externally: True, } options.add_experimental_option(prefs, prefs)我遇到过几次headless模式下点击下载按钮没反应的案例排查到最后都是下载配置没设好文件其实下载到了默认临时目录但脚本和测试报告里找不到看着就像“点击无效”。3.3 移动端模拟一套脚本模拟多种设备移动端兼容性测试如果没有真机ChromeDriver的mobile emulation可以帮你省掉一大笔设备费用。它的原理不是简单地把窗口缩小而是模拟移动设备的屏幕尺寸、像素密度、User-Agent甚至触摸事件。mobile_emulation { deviceName: iPhone 12 Pro } options.add_experimental_option(mobileEmulation, mobile_emulation) driver webdriver.Chrome(optionsoptions)也可以按需自定义设备参数mobile_emulation { deviceMetrics: { width: 390, height: 844, pixelRatio: 3.0 }, userAgent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 }用deviceName的好处是方便但不同Chrome版本对deviceName的支持列表不完全一样有时指定的机型在你的Chrome版本里不识别。我自己的习惯是尽量用自定义参数至少保证UA和屏幕尺寸是自己确认过的而不是赌Chrome内置的机型数据。移动模拟最常见的坑是页面里有hover交互、或者依赖click坐标的事件。模拟触摸端时某些hover展开的菜单会失效需要改成click先展开再点击子项。另外如果你的被测站点是PC版页面而不是移动版仅仅改UA是不够的有的站点会检测屏幕宽度来判断是不是移动端这个都取决于前端实现测试的时候建议先用浏览器手动访问确认一下。4. IE自动化兼容性问题的集中爆发区4.1 Protected Mode的四个区域设置这个坑我在2.2节简单提过但因为它真的特别容易让人崩溃我在这里展开讲一下。IE的安全区域分为四块Internet、本地Intranet、受信任的站点、受限制的站点。IEDriverServer启动时会检查四个区域的Protected Mode设置是否一致。也就是说你如果只把“Internet”区域的保护模式关了另外三个区域没动IEDriverServer照样会报错。我见过一个特别“经典”的场景同事在同事的电脑上跑脚本是好的换到自己电脑上立刻提示Protected Mode不一致检查了半天发现是IE设置里“以管理员身份运行”的选项影响了区域设置导致四块区域实际生效的配置不一致。解决办法打开IE - Internet选项 - 安全把四个区域全部选上“启用保护模式”或者全部取消然后重启IE。注意是“四个区域全部统一”哪怕某个区域你不打算访问也必须保持一致否则就是白调。还有一点容易被忽略调整完设置后注册表里的FEATURE_BROWSER_EMULATION可能还残留着旧的模式IE 11本身有些兼容性视图设置也会干扰。建议调整后重启系统和浏览器别偷懒。4.2 缩放比例问题与定位失败的关系这个坑特别“隐蔽”IE驱动要求浏览器缩放比例必须是100%否则元素坐标就会偏移。你在代码里用find_element_by_xpath找到元素然后click()脚本不报错但执行的点击位置却偏了——点到了别的元素上或者点到空白区域。为什么会这样因为WebDriver是基于坐标去执行原生鼠标事件的。缩放比例不是100%时IE的CSS像素和物理像素之间出现缩放换算而WebDriver协议里的坐标是以CSS像素计的IEDriverServer负责把坐标换算成系统屏幕坐标。一旦缩放比例不是100%换算结果就会偏。排查方法很简单看IE页面右下角的状态栏或者用JavaScript检查一下driver.execute_script(return window.devicePixelRatio;)正常情况devicePixelRatio应该是1如果大于1或小于1说明缩放比例不是100%。解决也简单在桌面空白处右键 - 显示设置 - 缩放与布局调成100%。或者让脚本在启动前用subprocess调用display命令去设置。不过现实中有些环境和远程桌面连在一起缩放比例经常被系统重置。所以更稳妥的办法是不要依赖手动改写个自动化前置检查发现比例不对就报警或者自动调整。4.3 IE特有的五个关键CapabilityIE驱动支持一堆IE专属Capability这些配置写不对运行时的诡异问题一个接一个。列五个我常用的Capability名称作用推荐值ignoreProtectedModeSettings跳过Protected Mode检查false除非真的没法统一设置ignoreZoomSetting跳过缩放比例检查false生产环境别开开了就是埋雷requireWindowFocus强制IE窗口获取焦点后执行操作true解决点击偶尔失效enablePersistentHover是否启用持久hoverfalseWindows 10上更稳定ensureCleanSession每次启动清理会话状态true避免上一个用例遗留的登录状态需要特别说明的是requireWindowFocus。IE在没有获得焦点的情况下某些鼠标事件根本不触发。你把浏览器窗口最小化然后跑脚本点击按钮按钮明明在DOM里能定位到但就是没有反应。把requireWindowFocus设成true驱动会尝试激活IE窗口这能解决大部分“元素找到了但点不动”的问题。但代价是打开这个选项后脚本执行期间你没法操作其他程序因为IE窗口会不断抢焦点。如果你需要在测试机边上干活那就会很痛苦。我一般是在CI机上开这个选项本地开发调试时关掉。4.4 在Windows下让IE自动化稳定运行的技巧IE自动化和Chrome自动化最大的区别在于Chrome的启动、退出、DOM操作基本都是标准化的IE则严重依赖操作系统的窗口管理器。我在实践中总结了一套相对稳定的运行条件满足这些条件的项目IE自动化的稳定性会明显提高第一用独立专机跑IE自动化不要同时跑其他GUI程序。Windows的窗口焦点机制决定了IE自动化需要窗口处于激活状态如果你开着其他程序抢焦点点击就会随机失效。第二关闭Windows的“自动隐藏任务栏”和“切换应用时动画效果”。这些视觉特性会干扰窗口管理器对窗口位置的计算。第三设置虚拟桌面分辨率为固定值比如1600x900不要用DPI缩放。Windows的DPI缩放一直是IE自动化的天敌能关就关。第四IE窗口打开后别手动去动它。脚本跑到一半你手贱去滚动一下IE页面后面的坐标全乱。这个看似无厘头实际上我踩过很多次。5. 实战脚本与常见问题排查5.1 一个完整的登录测试脚本示例纸上谈兵半天不如给一个能直接跑的脚本框架。下面这个示例我用Selenium 4的语法写同时兼容Chrome和IE通过参数切换浏览器类型import sys from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.options import Options as ChromeOptions from selenium.webdriver.ie.options import Options as IEOptions def create_driver(browser): if browser chrome: options ChromeOptions() options.add_argument(--window-size1920,1080) options.add_argument(--ignore-certificate-errors) options.add_argument(--disable-notifications) return webdriver.Chrome(optionsoptions) elif browser ie: options IEOptions() options.attach_to_edge_chrome False # 明确使用传统IE caps { requireWindowFocus: True, ignoreZoomSetting: False, enablePersistentHover: False, ensureCleanSession: True, } for key, value in caps.items(): options.set_capability(key, value) return webdriver.Ie(optionsoptions) else: raise ValueError(f不支持的浏览器: {browser}) def test_login(browser): driver create_driver(browser) try: driver.get(http://your-test-site.com/login) wait WebDriverWait(driver, 10) username wait.until(EC.presence_of_element_located((By.NAME, username))) username.clear() username.send_keys(testuser) password driver.find_element(By.NAME, password) password.clear() password.send_keys(Test123) driver.find_element(By.ID, login-btn).click() welcome WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, welcome-msg)) ) assert 欢迎 in welcome.text print(f[{browser}] 登录测试通过) finally: driver.quit() if __name__ __main__: test_login(sys.argv[1] if len(sys.argv) 1 else chrome)这个脚本虽然简单但已经包括了按浏览器创建driver、显式等待、断言、finally中退出driver。实际项目里你还需要加上日志、失败截图、重试机制。脚本只是一个基础骨架重点是养成一个习惯所有driver的创建和退出都放在一个可管理的地方不要散布在各处否则用例一多你就开始怀念这个看似多余的封装。这里再插一句封装经验强烈建议把create_driver封装成工厂函数而不是写一堆全局driver变量。全局driver的问题在于一条用例崩了之后driver不会自动退出下次再跑就会有一堆僵尸浏览器进程占用CPU和内存。用工厂上下文管理器模式能很好地规避。5.2 等待策略让脚本从“碰运气”变“稳定”脚本“有时候能跑有时候不能跑”十有八九是等待策略不对。Selenium里大致有三种等待方式隐式等待implicitly_wait设置一个全局的轮询超时driver.implicitly_wait(10)表示在10秒内如果元素没有出现会持续尝试查找超时才抛异常。显式等待WebDriverWait针对单个条件比如“等待按钮可点击”、“等待元素消失”、“等待URL包含某字符串”。强制等待time.sleep不推荐用来做业务逻辑但某些特殊场景不得不用比如依赖第三方动画或外部系统响应。我的经验分配是隐式等待设一个保底值比如10秒用于兜底关键路径全部用显式等待sleep尽量少用只在极少数“等待动画播放完”的场景里用而且给的时长要刻意调小传个500毫秒不要一上来就time.sleep(5)。显式等待比隐式等待强在哪里隐式等待只能控制“查找元素”的轮询但“元素找到了”和“元素可用”是两回事。你等到了a标签出现但它可能还是disabled状态你需要的是“可点击”。这种条件隐式等待完全管不了只能用显式等待配合element_to_be_clickable。wait WebDriverWait(driver, 10) login_btn wait.until(EC.element_to_be_clickable((By.ID, login-btn))) login_btn.click()另外显式等待的异常信息里通常会描述“等待了10秒、条件是什么、当前页面URL是什么”排查问题比裸奔的NoSuchElementException直观太多了。5.3 高频异常对照表直接上干活把最常见的异常和对应解法整理成一张表下次你再看到报错先翻这张表异常类型出现场景常见原因排查方向SessionNotCreatedException启动浏览器时ChromeDriver与Chrome版本不匹配核对两个版本号NoSuchWindowException切换窗口IE窗口被关闭或崩溃检查是否误点了关闭按钮NoSuchElementException定位元素等待不够或元素不在DOM先加显式等待再看选择器ElementNotInteractableException点击/输入元素被遮住或只读检查CSS遮挡、换个操作方式StaleElementReferenceException点击上一步找到的元素DOM被刷新旧引用失效重新定位元素TimeoutException等待条件条件没满足或等待超时确认页面是否真的触发相应变化WebDriverException任意阶段驱动进程异常退出查driver日志InvalidElementStateException输入元素不可编辑检查readonly属性要特别说一下StaleElementReferenceException这是新手最懵的异常之一。你明明上一步才找到的元素下一步就报“引用已过期”。原因通常是这个元素所在DOM被重新渲染了一次原来的对象被替换了。最典型的场景是点击刷新按钮后你还在用刷新前的元素引用。解法不复杂就是每个动作之前重新定位元素或者用wait.until里返回的一次性引用别把引用存在变量里用太多次。另一个要提醒的是ElementNotInteractableException它和NoSuchElementException的排查方向完全不同——前者是“找到了但没法操作”后者是“根本没找到”。很多人一报异常就以为定位器写错了实际上可能是页面弹出遮罩层挡住了元素。用JavaScript去检查遮挡物是最快的driver.execute_script( return document.elementFromPoint(arguments[0], arguments[1]);, element.rect[x] element.rect[width] / 2, element.rect[y] element.rect[height] / 2, )打印返回的标签名看看到底是什么挡了你一眼。6. 工程化建议让自动化测试可持续6.1 浏览器驱动的统一管理与自动更新自动化脚本写得再好如果驱动版本跟不上浏览器升级某天早晨你到公司发现所有用例“全军覆没”肯定会崩溃。所以驱动管理一定要有“版本策略”。我自己维护的驱动管理逻辑是这样第一记录当前环境里的浏览器版本找一个固定的基线版本。比如公司统一安装了Chrome 118那么环境里ChromeDriver就用118.x别在没人通知的情况下私自升级。第二驱动二进制放到一个统一的目录环境变量PATH指向它。Windows上我习惯放D:\tools\SeleniumDrivers\里面按浏览器分目录SeleniumDrivers\ ├─ chrome\ │ └─ chromedriver.exe ├─ ie\ │ ├─ 32\ │ │ └─ IEDriverServer.exe │ └─ 64\ │ └─ IEDriverServer.exe └─ README.md第三把版本号写进项目的配置文件启动时检查一次。一旦发现浏览器版本和驱动版本不匹配直接报警提示而不是等脚本跑挂了再去翻日志。可以用一个非常简单的脚本实现import shutil import subprocess browser_version subprocess.check_output( [reg, query, rHKLM\SOFTWARE\Google\Chrome\BLBeacon, /v, version], ).decode() # 这里只是示例实际取版本号的方式各环境不同 driver_version subprocess.check_output([chromedriver, --version]).decode()这种做法虽然“土”但非常实用。尤其是在超过10人的测试团队里每个人本地装的环境不一样没有一个统一的版本约定早晚出乱子。6.2 失败截图与日志的落地最后想给一个建议在自动化测试里截图和日志的价值往往被低估。你脚本跑挂了只有一段红色的异常堆栈排查效率极低。但如果你能在失败时自动把当前页面截图、把浏览器控制台日志、把DOM关键信息都保存下来排查成本能降一个量级。截图的做法很简单from selenium.webdriver.common.by import By from datetime import datetime import os try: # ... 业务动作 ... pass except Exception: timestamp datetime.now().strftime(%Y%m%d_%H%M%S) screenshot_dir ./screenshots os.makedirs(screenshot_dir, exist_okTrue) driver.save_screenshot(f{screenshot_dir}/failure_{timestamp}.png) with open(f{screenshot_dir}/page_{timestamp}.html, w, encodingutf-8) as f: f.write(driver.page_source) raise这里有几个细节值得注意save_screenshot拿到的是当前视口内可见区域的截图不是全页面截图。真要全页面截图得用特殊脚本。还有保存page_source时如果页面是异步渲染的拿到的HTML可能是加载完成前的状态所以最好配合等待条件。日志方面Selenium驱动本身是会输出日志的尤其是ChromeDriver有--verbose参数。如果你遇到浏览器启动失败、崩溃这类疑难杂症把ChromeDriver的日志加上service Service( executable_pathchrome_driver_path, service_args[--verbose, --log-path./logs/chromedriver.log] ) driver webdriver.Chrome(serviceservice)这样浏览器内部发生了什么就一目了然了。另外日志和截图要按时间归档。别把所有失败截图都堆到一个目录里久了之后分不清哪张对应哪次执行。我习惯按“日期/用例名”分层存储一天一个目录用例名前缀区分。7. 写在最后的一点经验Chrome和IE两个驱动一个代表“向前看”一个代表“向后兼容”。这几年我的体感是Chrome自动化越来越简单因为Chrome本身在稳定性和规范性上投入巨大新版本对WebDriver的支持越来越好而IE恰好相反微软自己都在推Edge了IE自动化的环境只会越来越“别扭”。如果你现在还在维护IE自动化我的忠告是尽量把用例隔离在一个小范围别把IE跑全量的回归测试因为IE的稳定性天花板摆在那里你不可能靠优化代码突破它的系统限制。等到你真的上手用我上面说的方式检查了Protected Mode、修正了缩放比例、调好了等待策略你会发现IE的很多“诡异”问题其实都有迹可循。它们不是随机发生而是你的脚本和IE之间有一条没对齐的“暗线”——这条暗线可能就是某个Capability、某个系统设置。最后无论你们团队用的是哪一套测试框架尽量把驱动管理、等待策略、异常处理做成公共模块不要每个用例各写各的。自动化测试久了你就知道用例数量增长不可怕可怕的是连维护成本一起指数级增长。把底子打好新旧浏览器混跑也能保持清晰的代码结构这对长期维护来说价值比任何一个单项优化都大。