ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python+Selenium从零入门:UI自动化核心原理与实操指南

Python+Selenium从零入门:UI自动化核心原理与实操指南 Selenium这个东西在测试圈里混了十几年依然是UI自动化绕不开的入门标配。就算现在Playwright、Appium这些新秀层出不穷Selenium的生态地位和招聘需求量依然非常坚挺。我见过太多人一上来就啃框架、折腾分布式结果写出来的脚本连别人电脑上都跑不起来。这篇文章抛开那些花里胡哨的东西从零开始带你用Python把Selenium的底子彻底打牢。不整虚的全是实操过的东西。1. 先搞清楚Selenium到底是干什么的1.1 UI自动化的核心价值很多刚入行的朋友对“自动化测试”有误解以为学会了它就能彻底替代手工点点点。实际上Selenium的核心价值在于回归测试和冒烟测试。假设你的产品每周迭代两次每次上线前都要把所有核心流程手工走一遍一个人一天就耗进去了。而Selenium脚本可以半夜自己跑早上起来看报告发现问题直接定位这才是它真正的意义。另外别指望拿Selenium去压测并发它是浏览器级别的操作走的不是协议层性能和并发完全不是它的菜。压测有LoadRunner或JMeterSelenium干的是把“人肉点鼠标”变成“代码点鼠标”。想清楚这一点你就不会对它抱有不切实际的期望。1.2 主流的Selenium家族成员咱们经常说的Selenium其实是一个家族主要有三兄弟Selenium WebDriver这是核心通过浏览器原生的驱动Driver来控制浏览器模拟真实用户操作。Selenium IDE一个浏览器插件可以录制你的操作然后生成脚本。适合快速验证思路但生成的代码通常要大量手工调整才能用在正式项目里。Selenium Grid用来做分布式执行多台机器、多种浏览器、多种平台同时跑同一批用例适合大型项目的兼容性测试。很多人分不清WebDriver和RCRemote Control的区别远古时代的RC是通过注入JavaScript来驱动的而WebDriver是直接调用浏览器原生接口速度更快、行为更接近真实用户。现在你学的WebDriver就对了RC那套早就淘汰了。2. 环境搭建这一步卡住了多少人2.1 Python环境和依赖安装如果你用的是Windows先去python.org下载Python 3.8以上版本安装时务必勾选“Add Python to PATH”这个勾不选后续你会被各种“不是内部或外部命令”的报错折磨疯掉。装完后打开命令行cmd或PowerShell输入python --version能返回版本号就说明PATH没问题。然后安装Selenium库pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple这里我习惯用清华镜像源国内直连PyPI有时候会慢到怀疑人生换源是常规操作。装完验证一下pip show selenium看到版本信息就OK了。这里多说一句不要用最新的Selenium版本就去下载最新的浏览器两者之间存在版本兼容性要求我后面会讲。2.2 浏览器驱动Driver的下载和坑Selenium WebDriver不能直接操作浏览器它需要一个中间的“翻译官”——驱动程序。我用的是Chrome浏览器对应需要下载ChromeDriver。这里有三个容易踩的坑第一版本必须匹配。你的浏览器是Chrome 120版本就得下载ChromeDriver 120.x版本。去对应版本页面下载最好把Chrome自动更新关掉否则某天浏览器悄悄升级了你的脚本就报“SessionNotCreatedException”了。第二驱动放到系统PATH里。最简单的做法是下载解压后把chromedriver.exe扔到Python的Scripts目录下这个目录通常已经在PATH里后续代码里就不用手动指定驱动路径了。第三别下载beta版本。有些朋友追求“最新”下载了beta版的ChromeDriver结果和稳定版浏览器完全不兼容白白浪费时间。确认驱动没问题后跑一段最基础的开浏览器代码验证环境from selenium import webdriver driver webdriver.Chrome() driver.get(https://www.baidu.com) driver.quit()如果能看到浏览器自动打开并访问百度然后自动关闭恭喜你环境算是通了。如果报错90%是驱动版本匹配问题。3. 第一个自动化脚本一步一步拆解3.1 从打开网页到定位元素环境通了以后我们就来写真正的自动化脚本。我以百度搜索为例因为百度对自动化测试的友好度很高没有太复杂的登录验证适合入门。from selenium import webdriver from selenium.webdriver.common.by import By import time driver webdriver.Chrome() driver.maximize_window() driver.get(https://www.baidu.com) # 找到搜索输入框输入关键词 search_input driver.find_element(By.ID, kw) search_input.send_keys(Selenium自动化测试) # 找到“百度一下”按钮点击 search_button driver.find_element(By.ID, su) search_button.click() time.sleep(3) print(页面标题, driver.title) driver.quit()这段代码里最关键的是find_element。By.ID是8种定位策略中的一种意思是“按ID属性找元素”。“kw”和“su”是百度登录页面输入框和按钮的ID值在浏览器里F12打开开发者工具选中元素就能看到。定位方式是Selenium的灵魂我后面专门细讲。3.2 为什么在脚本里加了time.sleep(3)这里我故意加了一个time.sleep(3)在实际项目里这种写法我会尽量避免用但入门阶段先不管那么多。为什么要等因为点击“百度一下”之后页面要加载新内容如果不等待立刻去取页面标题拿到的可能是跳转前的旧页面标题。这涉及自动化测试一个核心概念——同步问题。WebDriver操作浏览器的速度远远快于页面加载速度你必须要让脚本“等”页面。但是固定sleep是一种很低效的方式网络快了浪费时间网络慢了还是报错。后面的等待机制章节我会讲更优解法。3.3 脚本运行时的注意事项新手跑脚本时最常碰到的情况是浏览器一闪而过代码报错“Unable to find element。原因往往是页面还没加载完脚本就急着去找元素了。此时别慌把浏览器打开速度调慢或者加等待就好。另外driver.quit()很重要它的作用是关闭浏览器并释放驱动进程。如果你用driver.close()只是关掉当前标签页窗口没关干净。如果连着跑好几个脚本旧进程没释放会造成一堆残留的chromedriver进程占着内存跑几次就卡得不行。我建议每次脚本跑完后都检查一下任务管理器把残留的chromedriver进程清掉养成好习惯。4. 元素定位八种策略这是自动化的基本功4.1 常用的四种定位方式Selenium提供了8种定位元素的方法但对于日常测试真正高频使用的其实只有这几种定位策略方法示例适用场景IDfind_element(By.ID, kw)输入框、按钮、表单字段最稳定最推荐因为ID通常唯一Class Namefind_element(By.CLASS_NAME, btn)样式类名相同的元素适合批量元素或没有ID的元素Namefind_element(By.NAME, username)表单控件老项目里常见新项目较少XPathfind_element(By.XPATH, //input[idkw])任何元素几乎万能当其他方式不好使时的兜底方案XPath是我平时用得最多的定位方式。为什么因为现在的Web应用大量使用React、Vue这类框架很多元素的ID和Class都是动态生成的每次刷新页面就换一次。只有XPath能通过层级关系、文本内容、属性组合这些特征稳定地找到元素。打个比方ID定位相当于用门牌号找人XPath相当于用“穿红衣服、戴墨镜、站在吧台旁边那个人”这种特征来找人。现实中往往没有门牌号只能靠特征描述。4.2 XPath的进阶用法避开动态坑写XPath时最忌讳的是把整个绝对路径都写进去比如/html/body/div[1]/div[2]/form/input。这种路径只要页面结构稍微调整一下立马废掉。我更推荐相对路径属性组合的方式# 通过属性定位 element driver.find_element(By.XPATH, //input[placeholder请输入用户名]) # 通过文本定位 button driver.find_element(By.XPATH, //button[contains(text(), 登录)]) # 通过层级关系定位 input_box driver.find_element(By.XPATH, //div[classsearch-box]//input)这里推荐一个技巧用contains()做模糊匹配它的好处是哪怕元素的完整文本/属性值里有动态前缀或后缀只要包含关键字就能匹配上。比如“登录”和“立即登录”就能用contains统一处理。4.3 元素找不到先别着急遇到NoSuchElementException时我会按三条思路排查第一确认元素是不是真的在页面上。有的元素要在鼠标悬停之后才出现下拉菜单有的在iframe里面。我刚开始学的时候就栽过这个跟头怎么找都找不到后来才发现要找的元素在一个iframe里必须先切换进去才能定位。第二确认定位表达式是否准确。在浏览器开发者工具里按F12然后在Console里试一下$x(你的XPath表达式)能搜到元素说明表达式没问题搜不到就去改表达式。第三确认有没有被遮罩层挡住。有些弹窗或浮层会遮挡元素WebDriver点击时虽然不会报“找不到”但会报“ElementClickInterceptedException”这种情况需要先关闭弹窗再继续操作。4.4 定位不到元素时试试JavaScript有时候元素死活定位不到但页面上肉眼可见这往往是因为元素藏在某个容器里或者处于隐藏状态。此时我有个“作弊”方法直接用JavaScript来操作元素driver.execute_script(arguments[0].click();, target_element)这个方法绕过了WebDriver正常点击时的可见性检测能直接触发点击事件。这在处理一些特殊的UI组件比如某些自定义下拉框、滑动验证时非常实用。但不能什么问题都用这招毕竟脚本最终是为了模拟真实用户操作用JavaScript是“不得已而为之”不是首选方案。5. 等待机制三分钟讲透显式等待和隐式等待5.1 固定休眠为什么是一种坏味道前面代码里用了time.sleep(3)我强调这只是入门演示。真实项目中网页加载速度会因为服务器状态、网络波动有巨大起伏。固定等待3秒网络好时是浪费网络差时是报错两头不讨好。想象一下你等公交车固定睡眠相当于“不管车来没来先睡3分钟再说”显式等待则相当于“我盯着入口车一来我就上车”。效率差距显而易见。5.2 隐式等待的正确打开方式隐式等待是给WebDriver设置一个全局的“耐心值”。在脚本最开始设置一次之后每次查找元素时如果元素没出现WebDriver会持续轮询等待直到超过设定的时间阈值。driver.implicitly_wait(10)这段代码的意思是找一个元素时最多等10秒。优点是代码简洁不用在每次找元素时额外处理。缺点也明显它只对find_element有效对元素是否可见、是否可点击这些状态它是“睁一只眼闭一只眼”的。而且设置全局后整个会话内所有元素都按10秒来等偶尔会遇到某些元素本来应该快速报告“找不到”结果硬生生等了10秒拖慢整个用例执行。5.3 显式等待自动化测试的进阶必备显式等待才是真正聪明的等待方式它允许你对某一个具体元素设置等待条件和超时时间更加精准灵活。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, 10) login_button wait.until( EC.element_to_be_clickable((By.XPATH, //button[contains(text(), 登录)])) ) login_button.click()上面这段代码会等待最多10秒直到登录按钮变得可点击才执行点击。如果10秒后仍不可用就抛出TimeoutException。显式等待支持非常多的“预期条件”常用的有presence_of_element_located元素出现在DOM中visibility_of_element_located元素可见element_to_be_clickable元素可点击text_to_be_present_in_element元素中包含指定文本我的经验是显式等待合理的超时时间才是UI自动化最可靠的搭配。页面交互的核心状态就是“可见、可点、有渲染”把这三个条件控制好脚本的稳定性会大幅提升。6. 开始做项目把Selenium接入pytest和Allure6.1 为什么单个脚本不叫自动化测试很多人学到能点击几个元素就觉得自己会自动化了其实还差得很远。真正的自动化测试框架至少需要解决三件事用例组织、断言校验、报告输出。单个脚本只是“能跑”框架才解决“能看、能查、能定位”。pytest是Python测试领域最主流的框架没有之一。它对fixture夹具、参数化、插件生态的支持非常完善。结合Selenium你可以把每个用例写成独立函数再用pytest收集执行。6.2 写一个带断言的测试用例举个例子我们要验证搜索功能是否正常代码可以这样拆import pytest 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 pytest.fixture(scopeclass) def browser(): driver webdriver.Chrome() driver.implicitly_wait(10) yield driver driver.quit() class TestSearch: def test_baidu_search(self, browser): browser.get(https://www.baidu.com) search_input browser.find_element(By.ID, kw) search_input.send_keys(自动化测试) browser.find_element(By.ID, su).click() # 断言搜索结果标题包含关键词 WebDriverWait(browser, 10).until( EC.title_contains(自动化测试) ) assert 自动化测试 in browser.title注意两个关键点第一pytest.fixture里的yield是开闭原则的体现yield之前的代码在用例开始前执行之后的在用例结束后执行这里就是安全关闭浏览器。第二断言不是写不写都行的点缀它是自动化测试的灵魂没有断言的脚本只是“演示脚本”而不是“测试脚本”。6.3 Allure报告失败用例怎么快速定位跑完用例后光看命令行输出很难直观判断问题在哪。这里推荐接入Allure报告。先安装两个东西pip install allure-pytest然后在项目根目录执行测试并生成报告pytest --alluredir./results allure serve ./results这样运行后浏览器会自动打开一份漂亮的HTML报告包含每个用例的执行时间、状态、失败截图和日志。配置失败截图可以在pytest里挂钩子比如import allure import pytest pytest.hookimpl(hookwrapperTrue) def pytest_runtest_makereport(item, call): outcome yield report outcome.get_result() if report.when call and report.failed: if browser in item.funcargs: driver item.funcargs[browser] allure.attach(driver.get_screenshot_as_png(), name失败截图, attachment_typeallure.attachment_type.PNG)这属于“加分项”但确实重要。自动化测试最大的痛点之一是维护成本没有好报告出了问题你都不知道是产品Bug还是脚本自身的问题。7. 处理常见输入和弹出框不说你绝对想不到7.1 下拉框、单选按钮和日期控件在实际的项目里除了输入框和按钮更多的是下拉框、单选按钮、Checkbox、日历控件这些“形态各异”的元素。Selenium对这些元素的操作有各自的套路。下拉框用Select类来处理是最稳妥的别去点开再挨个点选项那纯属给自己找麻烦from selenium.webdriver.support.ui import Select select Select(driver.find_element(By.NAME, city)) select.select_by_visible_text(北京) select.select_by_value(110000) select.select_by_index(1)单选按钮和Checkbox就比较好处理拿到元素后执行click()就行。坑往往藏在日历控件上很多前端框架的日历控件根本就不是原生的input输入框。有些支持直接send_keys()往输入框里填值有些非得点开日历面板去选。遇到这种情况先试试send_keys不行再老老实实做日历的分步点击。还有一个老生常谈的危险操作弹窗。当你在页面上执行删除操作时浏览器通常会弹出一个确认框叫做Alert。处理方式如下import time # 触发弹窗 driver.find_element(By.XPATH, //button[text()删除]).click() time.sleep(1) # 切换到弹窗并点击确认 alert driver.switch_to.alert alert.accept()如果点错了也可以alert.dismiss()来取消。只要涉及“确定/取消”“删除/清空”这类的操作我都会预留弹窗处理逻辑不然脚本会在半路卡死。7.2 操作iframe里的元素iframe是一个常常被忽视但是又极其影响定位成功率的东西。很多项目都习惯用iframe嵌入其他系统的页面比如流程审批、地图展示、第三方支付等。如果元素嵌套在iframe里直接用find_element是找不到的必须先切换进去。# 先切换到iframe iframe driver.find_element(By.TAG_NAME, iframe) driver.switch_to.frame(iframe) # 这时候就可以定位iframe内的元素了 # 操作完之后切回默认主页面 driver.switch_to.default_content()切换到iframe时有几种做法按index从0开始、按TagName、按定位到的WebElement对象。我建议用WebElement来切只有这样才能保证切的是目标那个iframe。有一次排查了半天结果页面上好几个iframe用index切老是切错换成WebElement后一次就对了。7.3 文件上传的两种方式文件上传也是UI自动化里非常常见又容易踩坑的功能。分两种情况讨论。第一种是input typefile这种原生控件就直接用send_keys()传给绝对路径即可upload_input driver.find_element(By.NAME, file) upload_input.send_keys(C:\\Users\\test\\example.pdf)第二种是自定义的上传按钮点击后唤起的是系统文件选择窗口这种情况Selenium本身是“无能为力”的。因为Windows弹出的那个窗口不在浏览器DOM里Selenium管不到。此时要么用pywinauto或AutoIt去操作系统窗口要么调用类似driver.send_keys(text_path) Keys.ENTER的方式来触发快捷键粘贴。顺便说一下传路径时Windows路径里的反斜杠很容易因为Python转义问题出错。建议统一用原始字符串比如rC:\Users\test\example.pdf或者用双反斜杠。8. 自动化测试的稳定性经验之谈8.1 维护成本比开发成本更高三年前我做第一个自动化项目时天真地以为脚本写好就能一劳永逸。结果第二个星期开始每天早上一到办公室就是处理昨晚跑挂的脚本今天被改了按钮文案明天被换了弹窗样式后天下拉框选项顺序变了。那一段时间我对自动化的态度从“解放生产力”变成了“生产新的生产力负担”。后来我才明白一个朴素的真理自动化测试的核心技能不是写代码而是维护策略和代码设计。要让代码在前后端频繁变动中尽可能保持稳定不外乎三个原则第一定位方式优先用稳定属性——ID、Name、稳定不变的Class尽量避免用自动生成的动态值。尽量少用绝对路径XPath。第二页面操作尽量抽象封装。把“输入用户名”“点击登录”“校验登录成功”这种高频操作抽成独立方法页面一变只改封装层不用每个用例都改。第三数据和脚本分离。测试数据不要硬编码在脚本里放Excel、JSON或配置文件里脚本只是一套动作逻辑。8.2 关于AI工具和脚本自动生成的一点看法最近“AI自动化测试”“通过大模型自动生成UI脚本”这些话题在圈内热度蛮高。我也实际尝试过用大模型去生成定位表达式和测试用例确实能提升一部分效率。尤其针对一些结构规整的页面“你给我一个DatePicker我给你一段XPath”这类事情AI先生成我再校准能省不少时间。但UI自动化最大的问题从来不是“写不出定位”而是业务理解、异常情况预判和环境适配。AI它能知道你的业务流程是先校验手机号再展示验证码吗它能替你做异常场景的判断吗不能。所以我的观点是把AI当作“智能补全工具”用它提速但不能把测试设计完全交给它。这也是为什么即使AI炒得再热Selenium的底层原理仍然值得你花时间学透——基础永远是上层建筑的地基。8.3 稳定跑批必备的几个习惯最后分享几个我在实践中沉淀下来的小习惯每天晚上定时跑回归用例第二天早上看报告。手动跑脚本没有长期价值定时任务才产生自动化价值。失败用例自动重跑一次。网络抖动和偶发前端渲染慢导致的“假失败”比例很高重跑能大幅降低误报率。pytest里可以用pytest-rerunfailures插件pytest --reruns 1 --reruns-delay 5。把Browsers和WebDriver的版本记录在项目README里。等团队其他人接手时这一行字能帮你免掉很多“为什么我跑不起来”的白痴问题。对临时测试数据要清理。很多自动化跑着跑着就污染了环境第二次跑就开始报错了。用例末尾做好数据清理或者每次用随机数据都是好方案。9. 常见错误速查表我在带新人的过程中总结了一张高频错误速查表。新手遇到报错先来这里对号入座能解决一大半问题。报错信息出现原因解决方案SessionNotCreatedException浏览器与驱动版本不匹配下载与浏览器版本一致的ChromeDriver并替换NoSuchElementException元素定位不到检查定位表达式、确认是否在iframe中、加等待ElementNotInteractableException元素被遮挡、禁用或不可见检查是否有弹窗遮罩先关闭弹窗用显式等待等可点击ElementClickInterceptedException有其他元素挡住了目标元素滚动到元素可见位置或点击遮挡层关闭按钮StaleElementReferenceException页面已刷新之前拿到的元素对象已“过期”重新定位元素不要沿用旧的对象引用TimeoutException显式等待超时预期条件未满足检查等待的条件是否合理、页面是否真的加载出来了InvalidArgumentException传入了非法参数多半是send_keys传了非字符串转成字符串即可这些报错里StaleElementReferenceException是项目阶段最容易出现的。根因在于页面做过局部刷新或跳转原本那个元素“变了”但你的代码还在用“以前的那个记忆”。解决办法是每个步骤之前都重新获取一遍元素别图省事把元素对象存在变量里反复用。10. 给你的下一步建议学了这些内容你已经不是“从零”的状态了。接下来最好的进步方式是找到一个真实可用的Web系统比如自己公司产品的测试环境或者开源的电商系统把核心用户路径注册、登录、搜索、下单写成完全自动化的回归用例再接入pytest和Allure让它每晚自动跑。过程中你会遇到很多我上面提到的坑也会遇到很多我完全没提到的坑。这都很正常自动化测试本来就有一半时间在做“环境适配”和“问题排查”剩下的一半才是真正的用例设计。等你跑通了第一个完整的回归项目再回头看Selenium会发现它就是个非常能打的浏览器遥控器你真正值钱的不是遥控器本身而是你脑子里那套“怎么设计操作路径、怎么预判风险、怎么让脚本稳定”的思路。另外提醒一句别把Selenium当成唯一答案。碰到Windows桌面应用那就是另一个游戏碰到移动端AppAppium其实就是Selenium思路的延伸。底层的定位思想、等待策略、断言设计全是一脉相承的。把这一套学扎实再出去看其他工具你会发现很多都是老朋友换了身衣服。我个人在实际操作中的体会是入门Selenium最忌讳的是“光看不动手”。打开代码编辑器照着文章里的例子一点一点敲把报错一个个解决掉这个过程本身比任何教程都值钱。技术在快速迭代但“手动尝试—遇到问题—定位分析—解决沉淀”这条路永远不会过时。
返回列表