1. 项目概述:为什么你需要Selenium?
如果你是一名测试工程师、开发人员,或者任何需要与网页打交道的角色,那么“自动化”这个词对你来说一定不陌生。想象一下,每天需要重复登录某个后台、点击几十个按钮、填写无数表单来验证功能是否正常,这种机械劳动不仅枯燥,还容易因为人为疲劳而出错。Selenium 的出现,就是为了把我们从这种重复性劳动中解放出来。它不是一个单一的软件,而是一个强大的工具集,核心是 WebDriver,允许你用代码(比如 Python)像真人一样去操控浏览器——打开网页、点击链接、输入文字、提交表单,一切都可以自动化执行。
我接触 Selenium 已经超过八年,从最初的 Selenium RC 到现在的 WebDriver,见证了它从一个简单的录制回放工具,演变为一个支持 W3C 标准、生态繁荣的自动化测试框架。它绝不仅仅是“测试”工具,在数据抓取、监控巡检、RPA(机器人流程自动化)等领域同样大放异彩。网上很多教程只教你怎么写几行代码打开浏览器,但真正要在项目中落地,你会遇到浏览器版本不匹配、元素定位不到、脚本运行不稳定等一系列“坑”。这篇指南,我会结合我踩过的这些坑,带你从零开始,不仅学会如何使用 Selenium,更要知道如何用得稳、用得好。
2. 环境搭建与核心组件解析
在开始写第一行自动化脚本之前,搭建一个稳定、可复现的环境是成功的第一步。很多人在这里就放弃了,因为环境问题层出不穷。别担心,我会带你一步步走通。
2.1 Python与Selenium库安装
首先,确保你有一个干净的 Python 环境。我强烈建议使用virtualenv或conda创建独立的虚拟环境,避免不同项目间的包版本冲突。
# 创建虚拟环境 python -m venv selenium_env # 激活虚拟环境 (Windows) selenium_env\Scripts\activate # 激活虚拟环境 (Mac/Linux) source selenium_env/bin/activate激活环境后,安装 Selenium 库非常简单:
pip install selenium这里有个关键点:不要只安装selenium就以为万事大吉。Selenium 4.6 版本之后,引入了一个革命性的工具叫Selenium Manager。它是一个用 Rust 写的后台工具,当你创建浏览器驱动实例(如webdriver.Chrome())时,如果系统没有对应的浏览器驱动(如 chromedriver),Selenium Manager 会自动帮你下载匹配的版本。这大大降低了环境配置的复杂度。但在某些内网环境或特定需求下,你可能仍需手动管理驱动。
2.2 浏览器驱动的选择与管理
WebDriver 是 Selenium 的核心,它充当了你的代码和真实浏览器之间的“翻译官”。每个浏览器都需要对应的驱动:
- Chrome/Chromium:
chromedriver - Firefox:
geckodriver - Edge:
msedgedriver - Safari: Safari 浏览器内置(需在“开发”菜单中启用“允许远程自动化”)
手动管理驱动(传统方式):
- 查看你本地 Chrome 浏览器的版本(在地址栏输入
chrome://settings/help)。 - 去 ChromeDriver 官网 下载对应版本(主版本号必须完全一致)的驱动。
- 将下载的
chromedriver.exe(Windows) 或chromedriver(Mac/Linux) 文件放在一个目录下,并将该目录添加到系统的 PATH 环境变量中,或者直接在代码中指定驱动路径。
from selenium import webdriver from selenium.webdriver.chrome.service import Service # 指定驱动路径的方式 service = Service(executable_path=r'C:\path\to\chromedriver.exe') driver = webdriver.Chrome(service=service)使用 Selenium Manager(推荐,Selenium 4.6+): 如果你安装的是较新版本的 Selenium,直接初始化即可,库会尝试自动处理驱动。
from selenium import webdriver driver = webdriver.Chrome() # Selenium Manager 在背后默默工作注意:虽然 Selenium Manager 很方便,但在公司内网或持续集成(CI/CD)环境中,为了稳定性和下载速度,我们通常还是会将特定版本的浏览器驱动预先存放在项目里或固定的网络路径,并在代码中显式指定路径。避免因网络问题导致自动化任务失败。
2.3 IDE选择:VSCode与PyCharm
写 Python 脚本,一个好用的编辑器能事半功倍。
- VSCode:轻量、插件丰富。安装 Python 扩展和 Pylance 后,对 Selenium 的代码提示和自动补全支持很好。适合喜欢轻量级、高度自定义的开发者。
- PyCharm:专业 Python IDE,开箱即用,对 Web 开发框架和测试框架(如 pytest)的集成更深入。其专业版对前端调试也有很好支持。适合团队开发或复杂项目。
我个人在快速编写调试脚本时用 VSCode,在大型测试项目中使用 PyCharm。你可以根据习惯选择。
3. 第一个Selenium脚本:从“Hello World”到理解核心对象
让我们写一个最简单的脚本,并理解每一行代码背后的含义。
from selenium import webdriver from selenium.webdriver.common.by import By import time # 1. 创建浏览器驱动实例 driver = webdriver.Chrome() # 如果你使用Firefox # driver = webdriver.Firefox() try: # 2. 导航到目标网页 driver.get("https://www.baidu.com") print(f"当前页面标题是:{driver.title}") # 3. 定位元素并交互 # 找到百度搜索框 search_box = driver.find_element(By.ID, "kw") # 在搜索框中输入文本 search_box.send_keys("Selenium 自动化测试") # 找到百度一下按钮并点击 search_button = driver.find_element(By.ID, "su") search_button.click() # 4. 等待一下,观察结果 time.sleep(3) # 5. 验证结果(简单示例) # 假设我们想检查结果页是否包含特定文字 assert "Selenium" in driver.page_source print("测试通过!") finally: # 6. 关闭浏览器 driver.quit()代码逐行解析:
driver = webdriver.Chrome(): 这行代码启动了 Chrome 浏览器的一个无界面实例(除非你额外设置参数)。这个driver对象是你所有操作的入口,它代表了这个浏览器窗口(或标签页)。driver.get(url): 命令浏览器加载指定 URL。driver.title和driver.page_source是driver对象的属性,分别获取当前页面的标题和完整的 HTML 源码。find_element(By.ID, “kw”): 这是 Selenium 最核心的操作之一——元素定位。By.ID表示使用 HTML 元素的id属性来查找。“kw”就是百度搜索框的 id。find_element返回第一个匹配的元素对象,如果找不到会抛出NoSuchElementException。send_keys(“text”)和click(): 这是对元素对象的交互操作。send_keys用于输入文本(模拟键盘),click()用于模拟鼠标点击。time.sleep(3): 这是一个强制等待,让脚本暂停3秒。在实际自动化中,这是不推荐的做法,因为它效率低下且不可靠。我们后面会讲更智能的等待方式。driver.quit():至关重要!它会关闭浏览器并释放 WebDriver 会话占用的所有资源。务必在脚本最后执行,通常放在finally块中确保即使出错也会执行。与之对应的是driver.close(),它只关闭当前标签页,如果只有一个标签页则关闭浏览器,但不如quit()彻底。
4. 元素定位:自动化脚本的“眼睛”
元素定位是 Selenium 自动化脚本的基石。定位不准,后续所有操作都无从谈起。Selenium 提供了8种主要的定位策略(By类):
| 定位器 | 描述 | 示例 (By.XXX, “值”) | 适用场景与注意事项 |
|---|---|---|---|
| ID | 通过元素的id属性定位 | By.ID, “username” | 最高优先级。ID 在 HTML 中应唯一,定位最快、最准确。 |
| NAME | 通过元素的name属性定位 | By.NAME, “password” | 常用于表单元素,如 input、select。可能不唯一。 |
| CLASS_NAME | 通过元素的class属性定位 | By.CLASS_NAME, “btn-primary” | 一个元素可能有多个 class,此处需填写完整的 class 名(空格分隔的其中一个)。 |
| TAG_NAME | 通过 HTML 标签名定位 | By.TAG_NAME, “input” | 通常用于查找某一类元素,如所有输入框。返回第一个匹配元素。 |
| LINK_TEXT | 通过超链接的完整文本定位 | By.LINK_TEXT, “忘记密码?” | 仅用于<a>标签,文本必须完全匹配。 |
| PARTIAL_LINK_TEXT | 通过超链接的部分文本定位 | By.PARTIAL_LINK_TEXT, “忘记” | 同上,但只需包含部分文本即可。 |
| CSS_SELECTOR | 通过 CSS 选择器定位 | By.CSS_SELECTOR, “#loginForm .submit” | 功能最强大、最灵活。语法与前端 CSS 选择器一致,可以组合各种条件进行精准定位。 |
| XPATH | 通过 XML 路径语言定位 | By.XPATH, “//input[@name=‘email’]” | 同样强大灵活,可以在整个 DOM 树中导航。但表达式可能较复杂,性能略低于 CSS 选择器。 |
定位实战技巧与避坑指南:
- 优先顺序:
ID>NAME>CSS_SELECTOR>XPATH> 其他。ID 和 NAME 是最高效的。如果元素没有好的 ID 或 NAME,CSS Selector 通常是首选,因为它更简洁,浏览器原生支持,解析速度通常比 XPath 快。 - 使用浏览器开发者工具:按 F12 打开,使用“检查元素”功能(Ctrl+Shift+C)。你可以直接右键元素,选择“Copy” -> “Copy selector” 或 “Copy XPath”。但自动生成的路径往往很脆弱,一旦页面结构微调就可能失效。要学会自己编写健壮的选择器。
- 编写健壮的 CSS Selector:
input#email:选择 id 为 email 的 input 元素。.btn.btn-large:选择同时拥有btn和btn-large两个 class 的元素。form#loginForm > input[type=“submit”]:选择 id 为 loginForm 的 form 标签下,类型为 submit 的直接子 input 元素。ul.menu li:nth-child(2):选择 class 为 menu 的 ul 下第二个 li 元素。
- 编写健壮的 XPath:
//button[text()=‘登录’]:查找文本内容为“登录”的 button 元素。//div[@class=‘container’]//input[@placeholder=‘请输入用户名’]:查找 class 为 container 的 div 下的任意层级中,placeholder 属性为“请输入用户名”的 input。- 避免使用包含索引的绝对路径,如
/html/body/div[3]/div[2]/div[4],这种路径极其脆弱。
- 处理动态ID或Class:现代前端框架(如 React, Vue)常生成动态的 class 或 id。此时应寻找其他不变的属性,如
>driver.implicitly_wait(10) # 设置隐式等待10秒 element = driver.find_element(By.ID, “dynamicElement”)缺点:它是全局性的,会影响所有的
find_element操作。对于不需要等待的操作(如查找不存在的元素用于断言)也会产生不必要的等待。并且,它不适用于判断元素的状态(如是否可点击、是否可见)。5.3 显式等待 (Explicit Wait) —— 最佳实践
显式等待是针对某个特定条件进行的等待,更加灵活和精准。你需要用到
WebDriverWait类和expected_conditions模块。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 设置一个最长等待时间为10秒的等待对象 wait = WebDriverWait(driver, 10) # 等待直到某个元素可见并可交互 element = wait.until(EC.element_to_be_clickable((By.ID, “submitBtn”))) element.click() # 等待直到某个元素在DOM中存在(不一定可见) element_present = wait.until(EC.presence_of_element_located((By.CLASS_NAME, “alert”))) # 等待直到页面标题包含特定文字 wait.until(EC.title_contains(“订单提交成功”)) # 等待直到某个元素从DOM中消失(例如加载动画) wait.until(EC.invisibility_of_element_located((By.ID, “loadingSpinner”)))常用 expected_conditions (EC):
presence_of_element_located: 元素出现在 DOM 中。visibility_of_element_located: 元素可见(不仅存在,而且宽高大于0)。element_to_be_clickable: 元素可见且可点击(通常是visibility_of和enabled状态的结合)。text_to_be_present_in_element: 元素的文本包含特定字符串。alert_is_present: 等待警告框出现。
实操心得:在正式的自动化项目中,我几乎全部使用显式等待,并完全避免使用隐式等待。因为两者混用可能导致不可预料的等待时间叠加。我会为不同的操作定义不同的等待条件,例如,对于点击按钮,使用
element_to_be_clickable;对于获取刚加载完的列表,使用visibility_of_all_elements_located。这能让脚本在可靠性和执行效率之间取得最佳平衡。6. 高级交互操作:模拟真实用户行为
除了简单的点击和输入,Selenium 还能通过
ActionChains类模拟更复杂的用户交互,如鼠标悬停、拖放、右键点击、组合键等。6.1 鼠标操作
from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys # 假设我们有一个需要悬停才能显示的下拉菜单 menu = driver.find_element(By.ID, “dropdownMenu”) sub_menu_item = driver.find_element(By.LINK_TEXT, “高级选项”) # 创建 ActionChains 对象 actions = ActionChains(driver) # 鼠标悬停到菜单上,然后点击子项 actions.move_to_element(menu).pause(1).click(sub_menu_item).perform() # `perform()` 是执行所有已排队的动作 # 双击操作 element_to_double_click = driver.find_element(By.ID, “item”) actions.double_click(element_to_double_click).perform() # 右键点击(上下文菜单) actions.context_click(element_to_double_click).perform() # 拖放操作 source = driver.find_element(By.ID, “draggable”) target = driver.find_element(By.ID, “droppable”) actions.drag_and_drop(source, target).perform()6.2 键盘操作
键盘操作通常与
send_keys结合。from selenium.webdriver.common.keys import Keys search_box = driver.find_element(By.NAME, “q”) search_box.send_keys(“Selenium”) # 输入文字 search_box.send_keys(Keys.ENTER) # 模拟按下回车键 # 组合键操作,例如 Ctrl+A (全选) actions.key_down(Keys.CONTROL).send_keys(“a”).key_up(Keys.CONTROL).perform() # 或者直接在元素上操作 search_box.send_keys(Keys.CONTROL, “a”) # 全选输入框内容6.3 处理JavaScript弹窗
浏览器有三种原生弹窗:alert(警告)、confirm(确认)、prompt(提示)。
from selenium.webdriver.common.alert import Alert # 触发一个alert driver.find_element(By.ID, “triggerAlert”).click() # 切换到alert alert = Alert(driver) # 获取弹窗文本 print(alert.text) # 点击“确定” alert.accept() # 或者点击“取消”(针对confirm和prompt) # alert.dismiss() # 针对prompt,还可以输入文本 # alert.send_keys(“输入的文字”) # alert.accept()6.4 文件上传
文件上传有两种常见场景:
- Input 标签类型为 file:这是最简单的,直接使用
send_keys传入文件本地绝对路径即可。upload_element = driver.find_element(By.XPATH, “//input[@type=‘file’]”) upload_element.send_keys(r“C:\Users\YourName\Desktop\test_file.pdf”) - 非 Input 标签的复杂上传:可能需要借助
AutoIT、PyWin32等工具模拟操作系统级别的窗口操作,或者与开发协商在测试环境暴露一个直接的文件上传接口。这是一个难点,通常需要根据具体控件定制方案。
7. 框架设计与最佳实践
当你的自动化脚本从一个简单的 demo 演变成包含几十上百个测试用例的工程时,良好的框架设计就至关重要了。它能提升代码的可维护性、可读性和复用性。
7.1 Page Object Model (POM) 设计模式
这是 Selenium 自动化测试中最重要、最经典的设计模式。其核心思想是将页面抽象成类,将页面元素抽象成类的属性,将页面操作抽象成类的方法。测试脚本只调用这些方法,不直接操作元素。
一个简单的登录页面对象示例:
# pages/login_page.py from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) # 定位器 self.username_input = (By.ID, “username”) self.password_input = (By.ID, “password”) self.login_button = (By.ID, “loginBtn”) self.error_message = (By.CLASS_NAME, “alert-error”) def load(self): self.driver.get(“https://example.com/login”) return self def enter_username(self, username): element = self.wait.until(EC.visibility_of_element_located(self.username_input)) element.clear() element.send_keys(username) return self # 支持链式调用 def enter_password(self, password): element = self.wait.until(EC.visibility_of_element_located(self.password_input)) element.clear() element.send_keys(password) return self def click_login(self): element = self.wait.until(EC.element_to_be_clickable(self.login_button)) element.click() def get_error_message(self): try: element = self.wait.until(EC.visibility_of_element_located(self.error_message)) return element.text except: return None # 测试脚本中这样使用 # tests/test_login.py def test_valid_login(driver): login_page = LoginPage(driver).load() login_page.enter_username(“admin”).enter_password(“secret”).click_login() # 断言跳转到了主页 assert “Dashboard” in driver.title def test_invalid_login(driver): login_page = LoginPage(driver).load() login_page.enter_username(“wrong”).enter_password(“wrong”).click_login() error_msg = login_page.get_error_message() assert error_msg is not None assert “用户名或密码错误” in error_msgPOM 的优势:
- 代码复用:相同的页面逻辑只写一次。
- 易于维护:当页面元素发生变化时,只需修改对应的 Page 类中的定位器,所有测试用例无需改动。
- 可读性强:测试用例读起来像自然语言,业务逻辑清晰。
- 减少重复:将等待、日志记录等通用操作封装在 Page 方法中。
7.2 与测试框架集成:pytest
pytest是 Python 生态中最流行的测试框架之一,与 Selenium 结合能发挥巨大威力。基础集成:
- 安装 pytest:
pip install pytest - 使用
pytest的命令行运行测试脚本。 - 利用
fixture管理浏览器生命周期。
# conftest.py (项目根目录下的配置文件) import pytest from selenium import webdriver @pytest.fixture(scope=“function”) # 每个测试函数执行一次 def driver(): # 初始化浏览器 _driver = webdriver.Chrome() _driver.implicitly_wait(5) _driver.maximize_window() yield _driver # 将driver对象提供给测试用例 # 测试结束后清理 _driver.quit() # test_sample.py def test_baidu_search(driver): # pytest会自动注入fixture driver.get(“https://www.baidu.com”) assert “百度” in driver.title高级特性应用:
- 参数化测试:用
@pytest.mark.parametrize为同一个测试用例提供多组数据。import pytest @pytest.mark.parametrize(“username, password, expected”, [ (“admin”, “correct”, True), (“admin”, “wrong”, False), (“”, “”, False), ]) def test_login(driver, username, password, expected): # ... 使用参数执行登录逻辑并断言 - 失败截图与日志:使用
pytest的钩子函数,在测试失败时自动截图。# conftest.py import pytest from datetime import datetime @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() if report.when == “call” and report.failed: # 获取测试用例中的driver fixture driver_fixture = item.funcargs.get(“driver”) if driver_fixture: timestamp = datetime.now().strftime(“%Y%m%d_%H%M%S”) screenshot_path = f”./screenshots/failure_{item.name}_{timestamp}.png” driver_fixture.save_screenshot(screenshot_path) print(f”Screenshot saved to: {screenshot_path}“)
7.3 数据驱动测试
将测试数据(如用户名、密码、搜索关键词)与测试逻辑分离,通常存储在外部文件(如 JSON, YAML, CSV, Excel)或数据库中。
# 使用 JSON 文件存储测试数据 # data/login_data.json [ {“username”: “standard_user”, “password”: “secret_sauce”, “expected”: “success”}, {“username”: “locked_out_user”, “password”: “secret_sauce”, “expected”: “error”} ] # 测试用例中读取数据 import json import pytest with open(‘data/login_data.json’, ‘r’) as f: test_data = json.load(f) @pytest.mark.parametrize(“data”, test_data) def test_login_with_data(driver, data): login_page = LoginPage(driver).load() login_page.enter_username(data[“username”]) .enter_password(data[“password”]) .click_login() if data[“expected”] == “success”: assert “inventory” in driver.current_url else: assert login_page.get_error_message() is not None8. 常见问题排查与性能优化
即使按照最佳实践编写脚本,在实际运行中仍会遇到各种问题。这里记录了一些高频问题的排查思路和优化技巧。
8.1 元素定位失败(NoSuchElementException)
这是最常见的问题。
- 检查定位器:首先在浏览器开发者工具中,用
$x(‘你的XPath’)或$$(‘你的CSS选择器’)验证定位器是否能找到元素。 - 检查等待:元素是否还没加载出来?确保使用了合适的显式等待(如
visibility_of_element_located),而不是presence_of_element_located(元素在DOM但可能不可见/不可交互)。 - 检查iframe:目标元素是否在
<iframe>或<frame>中?如果是,需要先使用driver.switch_to.frame(frame_reference)切换到对应的 frame 内才能定位元素,操作完后再用driver.switch_to.default_content()切回主文档。 - 检查Shadow DOM:现代 Web 组件可能使用 Shadow DOM。Selenium 4 提供了
driver.execute_script执行 JavaScript 来穿透 Shadow Root,或者使用driver.find_element的扩展方法(如By.CSS_SELECTOR配合>>>或/deep/选择器,但浏览器支持度不一)。 - 检查页面是否跳转/刷新:在点击一个按钮后页面发生了跳转或刷新,之前的元素引用就失效了。需要在操作后重新定位元素。
8.2 脚本执行不稳定(Flaky Tests)
脚本有时成功有时失败,令人头疼。
- 强化等待:这是最主要的原因。将所有
time.sleep替换为针对特定条件的显式等待。 - 使用重试机制:对于某些不稳定的操作(如网络请求),可以使用
tenacity库或自己写简单的重试逻辑。from tenacity import retry, stop_after_attempt, wait_fixed @retry(stop=stop_after_attempt(3), wait=wait_fixed(2)) def click_unstable_button(driver): driver.find_element(By.ID, “unstableBtn”).click() - 优化定位器:避免使用绝对 XPath 或依赖于动态属性的定位器。寻找更稳定的标识,如
>from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument(“–disable-extensions”) chrome_options.add_argument(“–disable-notifications”) driver = webdriver.Chrome(options=chrome_options)
8.3 性能优化
当用例成百上千时,执行速度是关键。
- 复用浏览器会话:对于一组相关的测试,可以考虑不每个用例都重启浏览器,而是用
@pytest.fixture(scope=“class”)或@pytest.fixture(scope=“module”)来共享一个浏览器实例。但要注意用例间的状态隔离(清理 cookies、localStorage)。 - 使用无头模式 (Headless):在 CI/CD 环境或不需要观察UI时,使用无头模式可以节省大量资源。
chrome_options.add_argument(“–headless=new”) # Chrome 109+ 推荐方式 chrome_options.add_argument(“–disable-gpu”) # 早期版本可能需要 chrome_options.add_argument(“–no-sandbox”) # Linux环境常需要 chrome_options.add_argument(“–disable-dev-shm-usage”) # Docker环境常需要 - 并行执行:使用
pytest-xdist插件可以并行运行测试,大幅缩短总执行时间。pip install pytest-xdist pytest -n auto # 自动检测CPU核心数并行 - 减少不必要的截图和日志:只在失败或关键步骤时截图,避免每个步骤都产生大量日志文件。
8.4 处理验证码
自动化测试遇到验证码是一个经典难题。完全通用的解决方案不存在,以下是一些思路,按推荐度排序:
- 绕过:在测试环境中,让开发人员提供一个万能验证码(如“0000”)或直接关闭验证码功能。这是最直接有效的方法。
- 预留后门:在测试环境,通过调用一个内部接口获取当前会话的验证码(需要后端支持)。
- 使用第三方OCR服务:对于简单的图形验证码,可以截图后调用如 Tesseract(开源)或百度/腾讯的OCR API 进行识别。但识别率无法保证,且可能产生费用。
- 人工干预:对于极少数必须测试验证码的场景,可以设置一个超长等待,提示人工输入。这破坏了自动化流程,应尽量避免。
核心原则:自动化测试的目的是验证业务逻辑,而不是破解验证码。应尽可能在环境层面解决此问题。
9. 超越测试:Selenium的其他应用场景
虽然 Selenium 生于测试,但其能力远不止于此。
- 网页数据抓取:对于需要登录、有复杂 JavaScript 渲染、或反爬措施严格的网站,Selenium 是利器。它可以模拟真人操作,获取动态加载的数据。但请注意遵守网站的
robots.txt协议和法律法规。 - 自动化运维与监控:定期自动登录服务器管理后台检查状态,或巡检关键业务页面是否可访问、内容是否正确。
- RPA(机器人流程自动化):将那些规则明确、重复的线上人工操作自动化,例如定期从某个网站下载报表、批量处理后台订单等。
- 自动生成页面截图或PDF:结合无头模式,可以批量将网页保存为图片或打印成PDF,用于生成报告或存档。
10. 持续集成与交付 (CI/CD)
将 Selenium 自动化测试集成到 CI/CD 流水线中,是实现“质量左移”、快速反馈的关键。
- 环境准备:在 CI 服务器(如 Jenkins, GitLab CI, GitHub Actions)上安装浏览器(如 Chrome)和对应的 WebDriver,或使用 Docker 镜像(如
selenium/standalone-chrome)来提供一致的环境。 - 脚本与依赖:将你的测试代码、
requirements.txt(Python依赖)提交到代码仓库。 - 配置流水线:在 CI 配置文件中定义任务,通常包括:拉取代码 -> 安装依赖(
pip install -r requirements.txt) -> 运行测试(pytest) -> 生成报告。 - 测试报告:使用
pytest-html或allure-pytest生成美观的测试报告,并集成到 CI 界面中。pip install pytest-html pytest –html=report.html –self-contained-html - 失败处理:配置 CI 在测试失败时发送通知(邮件、钉钉、Slack),并自动归档失败时的日志和截图。
一个简单的 GitHub Actions 工作流示例 (.github/workflows/test.yml):
name: Selenium UI Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: ‘3.10’ - name: Install dependencies run: | pip install -r requirements.txt sudo apt-get update sudo apt-get install -y chromium-browser chromium-chromedriver - name: Run tests with pytest run: | python -m pytest tests/ -v –html=report.html –self-contained-html - name: Upload test report uses: actions/upload-artifact@v3 if: always() with: name: ui-test-report path: report.html走到这里,你已经从一个 Selenium 的初学者,成长为能够搭建稳健、可维护的自动化测试框架的实践者了。记住,自动化不是一蹴而就的,它是一个不断迭代、优化和积累的过程。从最重要的冒烟测试用例开始,逐步覆盖核心功能,让自动化真正成为你和团队提升效率、保障质量的可靠伙伴。在实际项目中,多与开发、产品沟通,让页面元素更“可测”(例如添加
>