1. 项目概述为什么现在学Selenium UI自动化测试依然有价值如果你是一名测试工程师或者是一名想提升效率的开发最近可能被各种AI测试工具、低代码平台的消息刷屏。很多人会问现在学Selenium这种“传统”的UI自动化测试框架是不是过时了我的答案是不仅没过时现在反而是打好基础、理解自动化测试本质的最佳时机。Selenium就像编程里的C语言它可能不是最高效、最酷炫的工具但它能让你透彻理解浏览器自动化是如何在底层工作的。当你理解了Selenium如何定位一个按钮、如何模拟一次点击你再去用那些封装好的AI测试工具就会明白它们到底在帮你做什么以及当它们“失灵”时问题可能出在哪里。这个项目就是带你从零开始搭建一个可运行、可维护的Selenium UI自动化测试脚本。我们不追求大而全的框架而是聚焦于解决一个核心问题如何让代码代替你的手在浏览器里完成一系列操作并告诉你结果对不对。你会学到环境搭建、元素定位、常用操作、等待机制以及如何组织简单的测试用例。学完之后你不仅能写出自己的第一个自动化脚本更能获得一种“自动化思维”这种思维是应对未来任何测试工具变迁的底层能力。2. 环境准备与核心工具链解析开始写代码之前先把“战场”布置好。一个稳定的环境是自动化测试成功的一半很多新手卡在第一步就是因为环境没配好。2.1 浏览器与驱动自动化测试的“手”和“脚”Selenium本身只是一个发出指令的“大脑”它需要两个关键部件才能操作浏览器浏览器被操作的对象如Chrome、Firefox。建议使用Chrome因为其生态最完善问题也最容易搜索到解决方案。浏览器驱动连接“大脑”和“手”的桥梁。Selenium通过驱动来控制浏览器。每个浏览器版本都需要对应版本的驱动。驱动下载与配置的黄金法则驱动版本必须与浏览器主版本号一致。比如你用的是Chrome 125版本就必须下载ChromeDriver 125.x.x.x版本。注意千万不要在搜索引擎里随便下载驱动一定要去官方或镜像站。最稳妥的方式是使用包管理工具或者从淘宝NPM镜像等可信源下载。这里以Chrome为例给出一个万无一失的配置流程查看你的Chrome版本在浏览器地址栏输入chrome://settings/help。根据版本号访问ChromeDriver的官方下载站或国内镜像站下载对应版本的驱动。将下载的chromedriver.exeWindows或chromedriverMac/Linux文件放在一个你记得住的目录比如D:\WebDriver。将这个目录的路径添加到系统的环境变量PATH中。这一步至关重要它让Python能在任何位置找到驱动。实操心得我习惯将驱动放在项目根目录下的一个drivers文件夹里然后在代码中指定绝对路径。这样做的好处是项目环境独立换机器时直接把整个文件夹拷走就行避免了因系统环境变量不同而导致的问题。2.2 Python与Selenium库测试脚本的“大脑”我们选择Python作为编程语言因为它语法简洁生态丰富是自动化测试领域的事实标准。安装Python从官网下载安装务必勾选“Add Python to PATH”。安装后在命令行输入python --version确认安装成功。安装Selenium库打开命令行运行pip install selenium。国内如果速度慢可以使用清华镜像源pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple。2.3 IDE的选择写代码的“书房”对于初学者我强烈推荐使用PyCharm社区版免费或VS Code。PyCharm开箱即用对Python支持极好调试功能强大。新建一个纯Python项目就可以开始写了。VS Code轻量灵活需要安装Python扩展但配置好后同样强大。选择哪一个取决于你的习惯。我的建议是如果你主要做Python开发PyCharm更省心如果你需要频繁切换多种语言VS Code更合适。3. 第一个Selenium脚本从“Hello World”到真实操作环境就绪我们来写第一个脚本。这个脚本的目标是打开百度搜索一个关键词并验证搜索结果页的标题。3.1 脚本骨架与核心对象WebDriver所有Selenium脚本都始于一个WebDriver对象。你可以把它想象成你赋予灵魂的一个“虚拟用户”。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time # 1. 创建WebDriver实例启动浏览器 # 指定驱动的绝对路径这是最稳妥的方式 driver webdriver.Chrome(executable_pathr‘D:\WebDriver\chromedriver.exe‘) # 如果你已将驱动加入PATH也可以简写为driver webdriver.Chrome() # 2. 控制浏览器打开目标网址 driver.get(‘https://www.baidu.com‘) # 最大化窗口避免元素因窗口太小而被隐藏 driver.maximize_window() # 等待2秒让页面加载这是初级做法后面会改进 time.sleep(2) # 3. 定位搜索框并输入关键词 # 通过元素的‘id‘属性定位这是最快最准的方式 search_box driver.find_element(By.ID, ‘kw‘) # 先清空搜索框避免有默认文本 search_box.clear() # 输入文本 search_box.send_keys(‘Selenium自动化测试‘) # 模拟键盘回车键进行搜索 search_box.send_keys(Keys.RETURN) # 4. 等待结果加载并验证 time.sleep(3) # 等待搜索结果加载 # 验证页面标题是否包含关键词 assert ‘Selenium自动化测试‘ in driver.title print(‘测试通过页面标题包含搜索关键词。‘) # 5. 查看当前页面URL可选用于调试 print(‘当前页面URL‘, driver.current_url) # 6. 关闭浏览器 driver.quit()逐行解析webdriver.Chrome()初始化一个Chrome浏览器的驱动对象。这是所有操作的起点。driver.get(url)让浏览器导航到指定URL。driver.find_element(By.ID, ‘kw‘)这是元素定位是UI自动化的核心。这里我们用By.ID定位器找到了百度首页搜索框其HTML的id属性是kw。send_keys()向输入框输入文本或模拟按键。driver.title获取当前浏览器标签页的标题。driver.quit()关闭浏览器并释放驱动资源。务必调用此方法否则后台会残留浏览器进程。第一个坑运行后你可能看到浏览器一闪而过。这是因为脚本执行速度很快执行到driver.quit()就结束了。我们用了time.sleep()来让脚本暂停以便观察。但在真实测试中绝对不要用sleep来等待页面加载这是极不稳定的做法。我们马上会讲更科学的等待方式。3.2 元素定位自动化测试的“眼睛”找不到元素一切操作都是空谈。Selenium提供了8种主要的定位方式我把它们分为三个梯队第一梯队首选稳定可靠By.ID通过HTML元素的id属性定位。id在理想情况下应该是唯一的定位速度最快。如果元素有id优先用它。By.NAME通过name属性定位。常用于表单元素。By.CLASS_NAME通过class属性定位。注意一个元素可能有多个class这里匹配的是其中一个。第二梯队灵活但需谨慎By.TAG_NAME通过标签名定位如input,a。通常一个页面有很多同类标签不唯一常与其他方法结合使用或用于查找多个元素。By.LINK_TEXT通过超链接的完整文本定位。如a href“...”登录/a可以用By.LINK_TEXT, “登录”。By.PARTIAL_LINK_TEXT通过超链接的部分文本定位。如上例“录”也能定位到。比LINK_TEXT宽松。第三梯队强大但复杂By.CSS_SELECTOR通过CSS选择器定位。功能极其强大可以组合各种条件是前端工程师的最爱。例如#kw表示id为kw的元素.s_ipt表示class包含s_ipt的元素。By.XPATH通过XML路径语言定位。功能最强大可以遍历整个DOM树定位任何元素。但表达式可能很复杂且性能稍差。例如//input[id‘kw‘]。定位策略建议优先级ID Name Class Name CSS Selector XPath Link Text Tag Name。如何查看元素属性在浏览器页面按F12打开开发者工具使用左上角的箭头工具点击页面元素即可在右侧的“Elements”面板看到其HTML代码和属性。绝对不要用浏览器自动生成的XPath开发者工具可以复制XPath但那些路径往往又长又脆弱页面结构微调就会失效。应该学习编写简洁、有弹性的XPath或CSS Selector。4. 核心操作与等待机制让脚本稳定运行学会了定位我们就要让元素“动”起来。同时解决页面加载的等待问题是脚本从“玩具”变为“工具”的关键。4.1 常用浏览器操作API除了之前用到的send_keys以下操作也极为常用点击element.click()清空输入框element.clear()获取元素文本element.text获取元素属性值element.get_attribute(‘attribute_name‘)例如获取一个链接的hrefget_attribute(‘href‘)判断元素是否可见/可用element.is_displayed(),element.is_enabled()浏览器导航driver.back()后退。driver.forward()前进。driver.refresh()刷新。窗口操作driver.maximize_window()最大化。driver.set_window_size(width, height)设置大小。driver.get_window_position(),driver.set_window_position(x, y)获取/设置位置。4.2 等待机制告别time.sleep拥抱智能等待time.sleep(秒数)是固定等待无论页面是否加载完成都要傻等那么久。这会导致测试效率极低且不稳定。Selenium提供了两种智能等待1. 隐式等待 (Implicit Wait)在创建WebDriver后设置一次对整个驱动生命周期有效。它告诉WebDriver在查找任何一个元素时如果元素没有立即出现就轮询等待一段时间比如10秒直到找到为止。如果超过时间还没找到才抛出异常。driver webdriver.Chrome() driver.implicitly_wait(10) # 单位秒 # 此后所有 driver.find_element 操作都会最多等待10秒2. 显式等待 (Explicit Wait)更精细、更强大的等待方式。你可以为某个特定的操作设置等待条件比如“等待这个按钮可点击”、“等待这个文本出现”。这是推荐的最佳实践。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待最多10秒直到ID为‘su‘的元素百度一下按钮可被点击 wait WebDriverWait(driver, 10) search_button wait.until(EC.element_to_be_clickable((By.ID, ‘su‘))) search_button.click() # 其他常用条件 # EC.presence_of_element_located((By.ID, ...)) # 元素出现在DOM中 # EC.visibility_of_element_located((By.ID, ...)) # 元素可见 # EC.title_contains(‘百度一下‘) # 标题包含某文字 # EC.alert_is_present() # 出现警告框等待策略黄金组合全局设置一个较短的隐式等待例如driver.implicitly_wait(5)作为兜底。在关键步骤如点击后页面跳转、异步加载使用显式等待指定明确的条件。彻底弃用time.sleep除非在极少数需要固定暂停的调试场景。4.3 处理弹窗、iframe和下拉框弹窗Alert/Confirm/Prompt# 切换到弹窗 alert driver.switch_to.alert # 获取弹窗文本 print(alert.text) # 点击确认 alert.accept() # 点击取消如果是confirm # alert.dismiss() # 输入文本如果是prompt # alert.send_keys(‘text‘)iframe内嵌框架 如果元素位于iframe内部你必须先“切换”到那个iframe上下文才能操作其中的元素。# 通过ID或Name切换 driver.switch_to.frame(‘iframe_id_or_name‘) # 通过元素对象切换 iframe_element driver.find_element(By.TAG_NAME, ‘iframe‘) driver.switch_to.frame(iframe_element) # 操作完iframe内的元素后切回主页面 driver.switch_to.default_content()下拉选择框Select 对于标准的select标签使用Select类更方便。from selenium.webdriver.support.ui import Select select_element driver.find_element(By.ID, ‘city‘) select Select(select_element) # 通过可见文本选择 select.select_by_visible_text(‘北京‘) # 通过value属性选择 # select.select_by_value(‘beijing‘) # 通过索引选择从0开始 # select.select_by_index(1)5. 构建可维护的测试用例与常见问题排查单个脚本跑通只是开始。如何组织多个测试用例以及如何应对执行过程中的各种问题才是工程化的体现。5.1 使用Unittest/Pytest组织测试用例我们不把代码全写在一个文件里。使用测试框架可以让用例管理、执行和报告更规范。这里以Python自带的unittest为例。创建一个文件test_baidu_search.pyimport unittest 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 class TestBaiduSearch(unittest.TestCase): 百度搜索测试用例类 def setUp(self): 每个测试方法执行前运行用于初始化 self.driver webdriver.Chrome() self.driver.implicitly_wait(5) self.driver.maximize_window() self.wait WebDriverWait(self.driver, 10) def tearDown(self): 每个测试方法执行后运行用于清理 self.driver.quit() def test_search_by_keyword(self): 测试通过关键词搜索 driver self.driver wait self.wait driver.get(‘https://www.baidu.com‘) search_box driver.find_element(By.ID, ‘kw‘) search_box.clear() search_box.send_keys(‘Python‘ Keys.RETURN) # 显式等待搜索结果标题出现 wait.until(EC.title_contains(‘Python‘)) self.assertIn(‘Python‘, driver.title) def test_search_empty_keyword(self): 测试搜索空关键词应停留在首页 driver self.driver driver.get(‘https://www.baidu.com‘) search_box driver.find_element(By.ID, ‘kw‘) search_box.clear() search_button driver.find_element(By.ID, ‘su‘) search_button.click() # 点击后应该还是百度首页 self.assertEqual(driver.title, ‘百度一下你就知道‘) if __name__ ‘__main__‘: unittest.main()这样你可以通过命令行运行所有测试python -m unittest test_baidu_search.py。setUp和tearDown保证了每个测试用例的独立性。5.2 常见问题与排查技巧实录即使代码写得再好运行时也会遇到各种“妖魔鬼怪”。下面是我踩过无数坑后总结的排查清单。问题现象可能原因排查步骤与解决方案NoSuchElementException(找不到元素)1. 定位表达式写错了。2. 元素在iframe里。3. 页面还没加载完元素不存在。4. 元素是动态生成的JS渲染。5. 页面有多个相同定位的元素找到的是第一个但不可操作。1.F12检查确认定位器是否能唯一定位到目标元素。2.检查iframe看目标元素是否在iframe内需要切换。3.增加等待使用显式等待条件设为元素可见或可点击。4.等待动态内容可能需要等待某个特定元素出现作为标志。5.改用find_elements获取列表后操作指定索引的元素。ElementNotInteractableException(元素不可交互)1. 元素被遮挡如弹窗、蒙层。2. 元素不可见display: none或visibility: hidden。3. 元素未处于可操作状态如disabled。1.滚动到元素driver.execute_script(“arguments[0].scrollIntoView();“, element)。2.等待遮挡物消失检查并关闭可能遮挡的弹窗。3.检查元素状态用is_displayed()和is_enabled()判断。4.尝试JS点击driver.execute_script(“arguments[0].click();“, element)终极手段。脚本被网站识别为自动化工具一些网站会检测Selenium的特征如window.navigator.webdriver属性。1.添加实验性选项初始化驱动时添加参数来隐藏特征此方法可能随浏览器更新失效。2.使用undetected-chromedriver一个专门应对检测的第三方库。3.降低操作频率在关键步骤间加入随机等待时间模拟真人操作。浏览器启动后立刻闪退1. 浏览器驱动版本与浏览器不匹配。2. 驱动路径错误或未加入PATH。3. 代码最后没有driver.quit()但进程被强制结束。1.核对版本严格检查Chrome和ChromeDriver的主版本号。2.指定绝对路径在代码中明确指定executable_path。3.使用try...finally确保无论如何quit()都会执行。运行速度慢1. 大量使用time.sleep。2. 网络环境差。3. 隐式等待时间设置过长。1.全面改用显式等待。2.优化定位器优先使用ID等快速定位方式。3.适当缩短隐式等待设置为3-5秒。一个高级调试技巧在代码中插入driver.save_screenshot(‘debug.png‘)可以在出错时截屏直观地看到当时浏览器页面的状态对于排查动态页面问题非常有用。6. 迈向下一步从脚本到框架的思考当你能够熟练编写并运行多个类似上面的测试用例后你可能会遇到新的问题测试数据怎么管理报告怎么生成用例怎么在服务器上定时跑这时你就需要向“测试框架”进化了。这不是一蹴而就的但你可以从这些方向逐步演进页面对象模型这是UI自动化测试最重要的设计模式。将每个页面或页面组件封装成一个类页面的元素定位和操作作为这个类的方法。测试脚本只调用这些方法不与具体的定位器耦合。这样当页面UI改动时你只需要修改对应的页面类而不需要修改大量测试脚本。数据驱动将测试数据如搜索关键词、登录账号从代码中分离出来存放在Excel、JSON、YAML或数据库中。测试脚本读取这些数据来执行实现一套脚本测试多组数据。测试报告使用HTMLTestRunner、Allure等库生成美观的HTML测试报告清晰地展示用例通过率、失败原因和截图。持续集成将你的测试框架接入Jenkins、GitLab CI等工具实现代码提交后自动触发测试或者每天定时执行并将结果通过邮件、钉钉等通知团队。入门Selenium你获得的不仅仅是一个工具的使用技能更是一套解决“如何让程序模拟人操作软件”这一问题的思维模型。这个模型是通用的无论将来你是去玩Appium做移动端自动化还是研究Playwright这个更现代的工具亦或是去理解那些宣称“AI自动化”的测试平台你都会发现万变不离其宗。核心永远是定位元素、执行操作、验证结果。把Selenium的基础打牢未来你在自动化测试的路上才能走得更稳、更远。