Selenium自动化测试入门:从WebDriver原理到Page Object实战
1. 项目概述:为什么我们需要Selenium?
如果你是一名软件测试工程师,或者是一名正在向测试开发转型的开发者,那么“UI自动化测试”这个词对你来说一定不陌生。在快速迭代的现代软件开发流程中,回归测试的工作量巨大且重复,纯靠人工点击不仅效率低下、容易出错,更会消耗团队宝贵的人力资源。这时,UI自动化测试就成了提升测试效率、保障软件质量的关键手段。而在众多UI自动化测试工具中,Selenium无疑是那个最闪亮、应用最广泛的明星。它不是一个单一的软件,而是一个强大的、支持多种编程语言和浏览器的Web应用自动化测试框架集合。简单来说,Selenium能让你用代码模拟一个真实用户,去操作浏览器,完成点击、输入、选择、拖拽等一系列动作,并验证页面的响应是否符合预期。
我最初接触Selenium,是为了解决一个每周都要重复上百次的登录和表单提交测试。手动操作不仅枯燥,还常常因为手滑点错而需要重来。当我用几十行Python脚本配合Selenium实现自动化后,原本需要半天的工作,现在喝杯咖啡的时间就完成了,而且结果准确无误。这种解放生产力的快感,是驱动我深入学习Selenium的最大动力。无论你是想告别重复的手工测试,还是希望构建更健壮的持续集成流水线,Selenium都是一个绕不开的核心技能。它适合所有与Web前端打交道的测试人员、开发人员以及任何希望提升Web操作自动化水平的学习者。
2. Selenium框架核心组件与生态解析
要玩转Selenium,不能只知其然,更要知其所以然。Selenium项目由几个核心组件构成,理解它们各自的分工和协作方式,是搭建稳定自动化测试脚本的基石。
2.1 Selenium WebDriver:与浏览器对话的桥梁
Selenium WebDriver是整个生态的绝对核心,也是我们编写脚本时主要交互的对象。你可以把它想象成一个“万能遥控器”。在WebDriver之前,早期的Selenium RC(Remote Control)是通过注入JavaScript来操控浏览器的,这种方式存在诸多限制和安全问题。WebDriver则采用了更底层、更直接的方式:它通过浏览器厂商官方提供的驱动程序,直接与浏览器内核进行通信。
这里的关键在于“驱动程序”,例如chromedriver对应 Chrome,geckodriver对应 Firefox。当你写下一行driver = webdriver.Chrome()时,背后发生的是:
- Selenium客户端库(如Python的
selenium包)启动对应的浏览器驱动进程。 - 驱动进程启动并控制真正的浏览器实例。
- 后续所有像
find_element,click,send_keys这样的命令,都会通过一个叫JSON Wire Protocol(或更新的W3C WebDriver协议)的标准协议,从你的脚本发送给驱动,再由驱动翻译成浏览器能理解的原生操作来执行。
这种架构的优势非常明显:因为它使用的是浏览器原生支持的控制接口,所以模拟出的用户行为极其逼真,几乎和真人操作无异,同时也更稳定、功能更强大。这也是为什么WebDriver能成为W3C推荐标准的原因。
2.2 Selenium IDE:快速入门的录制工具
对于完全的新手,或者需要快速生成一些测试用例原型时,Selenium IDE是一个很好的起点。它是一个浏览器插件(支持Chrome和Firefox),可以录制你在浏览器中的操作,并自动生成可回放的测试脚本。
它的工作流程很简单:点击录制按钮 -> 在网页上进行正常操作 -> 停止录制。IDE会记录下你的每一个步骤,并生成对应语言的代码(如Python、Java等)。然而,在实际项目中,我很少直接使用IDE生成的脚本进行交付,原因有三:
- 脚本脆弱:生成的脚本严重依赖于元素的定位方式(如冗长的XPath),页面结构稍有变动,脚本就可能失效。
- 缺乏编程逻辑:难以集成复杂的判断、循环、数据驱动等逻辑。
- 不易维护:代码结构比较固定,不适合融入大型的测试框架。
所以,我更倾向于将Selenium IDE定位为一个“探索和学习工具”。用它来快速了解页面操作对应哪些Selenium命令,或者生成一个脚本草稿,然后在此基础上进行大刀阔斧的重构和优化,融入Page Object模式等最佳实践。
2.3 Selenium Grid:实现分布式并发测试
当你的测试用例成百上千,或者需要在多种浏览器、多种操作系统组合上进行测试时,单机执行会变得非常耗时。Selenium Grid就是为了解决这个痛点而生的。它采用Hub-Node(中心-节点)架构:
- Hub:中心调度器,负责接收测试脚本发来的请求。
- Node:测试节点,注册到Hub上。每个Node都配置了特定的浏览器类型、版本和操作系统信息。
你的测试脚本不再直接控制本地浏览器,而是将命令发送给Grid Hub。Hub会根据你的测试需求(例如,“需要在Windows 10的Chrome 120上运行”),寻找匹配的Node,并将命令转发给它执行。这样,你就可以:
- 并行执行:同时在多个Node上运行不同的测试,极大缩短总测试时间。
- 跨平台/浏览器测试:轻松搭建一个包含Windows、macOS、Linux上各种版本Chrome、Firefox、Edge的测试矩阵,确保Web应用的跨平台兼容性。
在持续集成环境中,结合Jenkins、GitLab CI等工具,Selenium Grid能自动触发大规模的并发兼容性测试,是保障产品质量的重要防线。
注意:虽然Selenium 4之后Grid的功能被集成得更好,但对于新手,我建议先从本地WebDriver开始,熟练后再研究Grid的部署和配置,避免一开始就陷入复杂的环境问题。
3. 从环境搭建到第一个脚本:手把手实战
理论说得再多,不如动手跑一遍。让我们从一个最简单的例子开始,感受Selenium的魅力。
3.1 环境准备与驱动配置
以最常用的Python + Chrome组合为例:
安装Python Selenium库:这是与WebDriver通信的客户端。
pip install selenium下载浏览器驱动:这是最关键的一步,驱动版本必须与你的浏览器大版本号匹配。
- 打开Chrome,在地址栏输入
chrome://version/,查看“Google Chrome”后面的版本号(例如,120.0.6099.109)。 - 访问ChromeDriver官方下载站或国内镜像站,下载对应版本(主要版本号120匹配即可)的
chromedriver。 - 将下载的
chromedriver.exe(Windows) 或chromedriver(macOS/Linux) 文件放在一个目录下,并将该目录添加到系统的环境变量PATH中。更简单的做法是,在代码中指定驱动路径。
- 打开Chrome,在地址栏输入
为什么版本匹配如此重要?因为ChromeDriver实现了Chrome浏览器用于自动化的DevTools Protocol接口。不同版本的Chrome,其Protocol可能有细微改动。版本不匹配最常见的错误就是This version of ChromeDriver only supports Chrome version XXX,导致浏览器无法启动。
3.2 编写并运行你的第一个自动化脚本
创建一个first_test.py文件,输入以下代码:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time # 1. 创建WebDriver实例,启动浏览器 # 指定chromedriver路径,如果已在PATH中,则只需 webdriver.Chrome() driver = webdriver.Chrome(executable_path=r'你的chromedriver路径') # 2. 打开目标网页 driver.get("https://www.baidu.com") print("当前页面标题是:", driver.title) # 3. 定位元素并交互 # 通过元素ID定位搜索框 search_box = driver.find_element(By.ID, "kw") # 在搜索框中输入文本 search_box.send_keys("Selenium自动化测试") # 模拟按下回车键 search_box.send_keys(Keys.RETURN) # 4. 等待一下,观察结果 time.sleep(3) # 5. 验证结果(简单示例) assert "Selenium" in driver.title # 6. 关闭浏览器 driver.quit()逐行解析:
webdriver.Chrome():初始化Chrome浏览器驱动。这是所有操作的起点。driver.get(url):让浏览器导航到指定URL,等同于在地址栏输入网址。driver.find_element(By.ID, "kw"):这是元素定位的核心。我们通过元素的ID属性(id="kw")找到了百度首页的搜索输入框。By类提供了多种定位方式。send_keys():向输入框输入文本或模拟按键。driver.quit():关闭浏览器并释放驱动进程资源。务必使用quit()而非close(),close()只关闭当前标签页。
运行这个脚本,你会看到Chrome浏览器自动打开,访问百度,输入搜索词并回车,然后停留3秒后关闭。恭喜你,已经完成了第一次Web自动化操作!
3.3 元素定位:自动化测试的基石
上面脚本中用到的find_element是Selenium中最重要、最常用的方法。找不到元素,一切操作都无从谈起。Selenium提供了8种主要的定位策略:
| 定位方式 | By类中的属性 | 示例 | 适用场景与优缺点 |
|---|---|---|---|
| ID | By.ID | find_element(By.ID, “userName”) | 首选。ID通常唯一,定位最快、最稳定。 |
| Name | By.NAME | find_element(By.NAME, “password”) | 次选。Name也可能不唯一,需确认。 |
| Class Name | By.CLASS_NAME | find_element(By.CLASS_NAME, “btn-primary”) | 类名常不唯一,易变,慎用。 |
| Tag Name | By.TAG_NAME | find_element(By.TAG_NAME, “input”) | 用于查找特定类型的元素,如所有输入框。 |
| Link Text | By.LINK_TEXT | find_element(By.LINK_TEXT, “忘记密码?”) | 精准定位纯文本链接。 |
| Partial Link Text | By.PARTIAL_LINK_TEXT | find_element(By.PARTIAL_LINK_TEXT, “忘记”) | 链接文本的部分匹配。 |
| XPath | By.XPATH | //div[@id=‘content’]/form/input[1] | 功能最强大,可定位页面任何元素。但易受页面结构调整影响。 |
| CSS Selector | By.CSS_SELECTOR | #content > form > input.btn | 效率通常比XPath高,语法简洁,推荐与ID、Class结合使用。 |
定位策略选择的心得:
- 优先级:ID > Name > CSS Selector > XPath > 其他。尽可能使用开发同事提供的唯一属性。
- 避免绝对XPath:类似
/html/body/div[3]/div[2]/form/div[1]/input这种绝对路径极其脆弱,页面任何位置增加一个div都会导致定位失败。应使用相对XPath或结合属性,如//input[@id=‘kw’]。 - 使用开发者工具辅助:在浏览器中按F12,使用“检查”工具点击元素,可以直接在Elements面板右键复制其XPath或CSS Selector,作为参考起点,但通常需要人工优化。
4. 核心操作与高级等待机制
掌握了定位,我们就可以让元素“动”起来。Selenium提供了丰富的API来模拟用户交互。
4.1 常用浏览器操作API
除了上面用到的get()和quit(),还有一些高频操作:
# 导航相关 driver.back() # 后退 driver.forward() # 前进 driver.refresh() # 刷新 # 窗口和框架相关 driver.maximize_window() # 最大化窗口 driver.get_window_size() # 获取窗口尺寸 driver.set_window_size(1024, 768) # 设置窗口尺寸 driver.switch_to.window(driver.window_handles[-1]) # 切换到最新打开的窗口 driver.switch_to.frame(“frame_name_or_id”) # 切换到iframe内 driver.switch_to.default_content() # 切回主文档 # 获取信息 current_url = driver.current_url page_source = driver.page_source # 获取页面HTML源码4.2 元素交互操作
找到元素后,可以对其进行多种操作:
element = driver.find_element(By.ID, “someId”) # 基础操作 element.click() # 点击 element.send_keys(“text”) # 输入文本 element.clear() # 清空输入框 element.submit() # 提交表单(如果该元素在form内) # 获取元素状态和信息 text = element.text # 获取元素可见文本 attr_value = element.get_attribute(“href”) # 获取属性值 is_displayed = element.is_displayed() # 是否可见 is_enabled = element.is_enabled() # 是否可用(如按钮) is_selected = element.is_selected() # 是否被选中(如复选框)4.3 处理弹窗与警告框
JavaScript弹出的alert,confirm,prompt需要特殊处理:
from selenium.webdriver.common.alert import Alert # 触发一个alert driver.find_element(By.ID, “triggerAlert”).click() # 切换到alert对象 alert = Alert(driver) # 获取alert文本 print(alert.text) # 接受(点击确定) alert.accept() # 取消(点击取消) # alert.dismiss() # 在prompt中输入文本 # alert.send_keys(“输入内容”) # alert.accept()4.4 等待机制:解决异步加载的利器
这是Selenium实战中最容易出错的部分。现代网页大量使用Ajax和前端框架,元素不会立即出现。如果脚本在元素加载完成前就去操作它,就会抛出NoSuchElementException。
1. 强制等待 (time.sleep)
import time time.sleep(5) # 无条件等待5秒不推荐。无论元素是否已就绪,都必须等待固定时间,浪费效率且不可靠。
2. 隐式等待 (implicitly_wait)
driver.implicitly_wait(10) # 设置全局等待时间为10秒设置后,在整个WebDriver生命周期中,每次执行find_element等定位操作时,如果找不到元素,会轮询等待(默认0.5秒检查一次)直至超时。它是个全局设置,只需一次。缺点是它只对“查找”操作有效,对元素状态(如可点击)无效。
3. 显式等待 (WebDriverWait)这是最推荐、最灵活的方式。它允许你为某个特定条件设置等待。
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待最多10秒,直到ID为‘dynamicElement’的元素出现在DOM中并可见 element = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, “dynamicElement”)) ) # 找到元素后,可以立即操作 element.click() # 其他常用条件 EC.presence_of_element_located((By.ID, “id”)) # 元素存在于DOM,不一定可见 EC.element_to_be_clickable((By.ID, “buttonId”)) # 元素可见且可点击 EC.title_contains(“Selenium”) # 页面标题包含某文字 EC.alert_is_present() # 等待alert出现我的等待策略实践:
- 全局设置一个较短的隐式等待,例如5秒,作为基础保障。
- 对于关键交互点,特别是涉及异步加载的元素,必须使用显式等待。这能确保脚本在元素真正就绪时才操作,极大提升稳定性。
- 避免
time.sleep出现在核心测试逻辑中,仅用于调试或观察。
5. 实战进阶:Page Object模式与数据驱动
当测试脚本越来越多,直接在与操作混合编写脚本的缺点就会暴露:重复代码多、可读性差、维护成本高。页面结构一变,需要修改无数个脚本。这时,我们需要引入设计模式。
5.1 Page Object Model:让测试脚本更优雅
Page Object Model的核心思想是将页面封装成对象,页面的元素定位和操作细节封装在对应的类中,测试脚本只调用这些对象提供的方法。这样实现了业务逻辑(测试步骤)与页面细节(定位器)的分离。
以一个登录页面为例:
# 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, *locator): return self.wait.until(EC.presence_of_element_located(locator)) def find_clickable_element(self, *locator): return self.wait.until(EC.element_to_be_clickable(locator)) # login_page.py - 登录页面对象 from selenium.webdriver.common.by import By from base_page import BasePage class LoginPage(BasePage): # 定位器:所有元素定位信息集中管理 USERNAME_INPUT = (By.ID, “username”) PASSWORD_INPUT = (By.ID, “password”) LOGIN_BUTTON = (By.ID, “loginBtn”) ERROR_MSG = (By.CLASS_NAME, “error-message”) def __init__(self, driver): super().__init__(driver) def enter_username(self, username): self.find_element(*self.USERNAME_INPUT).send_keys(username) def enter_password(self, password): self.find_element(*self.PASSWORD_INPUT).send_keys(password) def click_login(self): self.find_clickable_element(*self.LOGIN_BUTTON).click() def get_error_message(self): return self.find_element(*self.ERROR_MSG).text # 一个完整的业务操作流程 def login(self, username, password): self.enter_username(username) self.enter_password(password) self.click_login() # test_login.py - 测试脚本 import pytest from selenium import webdriver from login_page import LoginPage class TestLogin: def setup_method(self): self.driver = webdriver.Chrome() self.driver.implicitly_wait(5) self.login_page = LoginPage(self.driver) self.driver.get(“http://example.com/login”) def teardown_method(self): self.driver.quit() def test_login_success(self): self.login_page.login(“correctUser”, “correctPass”) # 断言:登录后应跳转到首页,通过URL或页面元素判断 assert “dashboard” in self.driver.current_url def test_login_failed(self): self.login_page.login(“wrongUser”, “wrongPass”) error_text = self.login_page.get_error_message() assert “用户名或密码错误” in error_textPOM模式的优势:
- 高可维护性:页面元素定位符只存在于Page类中。页面UI变更时,只需修改对应的Page类,所有测试脚本无需改动。
- 高可读性:测试脚本读起来像自然语言(
login_page.login(...)),清晰表达了“做什么”,而不是“怎么做”。 - 低冗余:页面通用操作(如等待、查找)可以抽象到基类中,避免重复代码。
5.2 数据驱动测试:分离测试逻辑与测试数据
数据驱动测试将测试数据(输入、预期结果)从测试脚本中剥离出来,存储在外部的文件(如JSON、YAML、Excel、CSV)或数据库中。同一个测试逻辑,可以用多组不同的数据来执行。
以使用pytest和@pytest.mark.parametrize装饰器实现数据驱动为例:
# test_data.py - 存储测试数据 login_test_data = [ (“correctUser”, “correctPass”, True, “”), # 成功用例 (“”, “somePass”, False, “用户名不能为空”), # 用户名为空 (“wrongUser”, “wrongPass”, False, “登录失败”), # 错误凭证 ] # test_login_ddt.py - 数据驱动测试脚本 import pytest from login_page import LoginPage class TestLoginDataDriven: @pytest.fixture(autouse=True) def setup(self, driver): # 假设driver是一个pytest fixture self.driver = driver self.login_page = LoginPage(driver) self.driver.get(“http://example.com/login”) yield # teardown 可以在这里,也可以由外部fixture管理 @pytest.mark.parametrize(“username, password, expected_success, expected_error”, login_test_data) def test_login_with_data(self, username, password, expected_success, expected_error): self.login_page.login(username, password) if expected_success: # 预期成功,验证跳转或成功元素 assert “dashboard” in self.driver.current_url else: # 预期失败,验证错误信息 actual_error = self.login_page.get_error_message() assert expected_error in actual_error这样,我们只需要维护login_test_data这个数据列表,就能轻松扩展测试用例。要增加一个测试场景,只需加一行数据,无需编写新的测试函数。
6. 常见问题排查与高级技巧
即使遵循了最佳实践,在实际项目中你依然会遇到各种“坑”。下面分享一些我踩过的坑和总结的技巧。
6.1 元素定位失败问题排查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 | ||
|---|---|---|---|---|
NoSuchElementException | 1. 定位表达式写错。 2. 元素在iframe/frame内。 3. 元素尚未加载出来(异步)。 4. 元素是动态生成的,ID/Class变化。 | 1. 用浏览器开发者工具检查定位器是否正确。 2. 使用 driver.switch_to.frame()切换到对应frame。3.使用显式等待等待元素出现。 4. 使用更稳定的定位方式,如XPath结合部分属性匹配( contains)、CSS Selector,或与开发约定测试属性如> | 1. 元素不可见(被遮挡、CSS隐藏)。 2. 元素未处于可交互状态(如disabled)。 | 1. 检查元素样式display: none或visibility: hidden。2. 等待元素变为可交互状态: EC.element_to_be_clickable。3. 尝试用JavaScript直接操作: driver.execute_script(“arguments[0].click();”, element)。 |
StaleElementReferenceException | 之前找到的元素“过期”了。通常发生在: 1. 页面刷新或跳转后。 2. 元素被重新渲染(如React/Vue动态更新DOM)。 | 1. 这是Selenium常见难题。解决方案是重新查找元素。在Page Object中,每次调用方法时都重新定位,而不是将元素对象存储在变量中长期使用。 2. 使用 try...catch捕获此异常,并在catch块中重新定位并重试操作。 | ||
| 脚本在IDE运行成功,在CI/CD失败 | 1. 环境差异(浏览器版本、驱动版本)。 2. CI环境可能是无头模式或分辨率不同。 3. 网络或资源加载速度慢。 | 1.固定版本:在CI中明确指定浏览器和驱动的版本。 2.增加等待时间,特别是隐式和显式等待的超时时间。 3. 在CI脚本中加入失败截图功能,便于排查。 4. 考虑使用 driver.set_window_size()设置固定分辨率。 |
6.2 处理动态元素与复杂交互
1. 应对动态ID/Class:如果元素的ID是类似“button-12345-random”这样每次刷新都变化的,就不能用完整ID定位。
- 使用XPath函数:
//button[starts-with(@id, ‘button-’)]或//button[contains(@class, ‘btn-primary’)]。 - 使用CSS Selector属性选择器:
button[id^=‘button-’](匹配id以‘button-’开头的button)。 - 最佳实践:与前端开发沟通,为重要的可测试元素添加固定的、语义化的属性,如
># 滚动到元素可见 element = driver.find_element(By.ID, “footer”) driver.execute_script(“arguments[0].scrollIntoView(true);”, element) # 修改元素属性(例如,让一个隐藏的输入框可见以便操作) driver.execute_script(“document.getElementById(‘hiddenInput’).style.display=‘block’;”) # 获取页面性能数据 load_time = driver.execute_script(“return performance.timing.loadEventEnd - performance.timing.navigationStart;”) print(f”页面加载耗时:{load_time}ms”)3. 文件上传:对于
<input type=“file”>元素,不要尝试去点击它打开系统对话框,而是直接使用send_keys传入文件本地绝对路径。upload_element = driver.find_element(By.ID, “file-upload”) upload_element.send_keys(“/Users/yourname/Downloads/test_file.pdf”)系统文件选择对话框是操作系统级别的控件,Selenium无法直接操作。
6.3 无头模式与截图功能
无头模式:在不显示浏览器GUI的情况下运行测试,节省资源,尤其适合CI/CD环境。
from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument(“--headless”) # 启用无头模式 chrome_options.add_argument(“--disable-gpu”) # 早期版本需要,现在可选 chrome_options.add_argument(“--window-size=1920,1080”) # 设置窗口大小 driver = webdriver.Chrome(options=chrome_options)截图功能:测试失败时,截图是定位问题的黄金标准。
# 截取整个屏幕 driver.save_screenshot(“./screenshot.png”) # 截取特定元素 element = driver.find_element(By.ID, “errorPanel”) element.screenshot(“./element_screenshot.png”) # 在pytest中使用,结合钩子函数自动截图 import pytest from selenium import webdriver @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() if rep.when == “call” and rep.failed: driver = item.funcargs[“driver”] # 假设driver是fixture if driver: driver.save_screenshot(f”./failures/{item.name}.png”)7. 集成到CI/CD与测试报告
自动化测试只有集成到开发流程中,才能持续发挥价值。通常我们会将其与持续集成工具结合。
7.1 与Jenkins/GitLab CI集成
核心思路是:在CI服务器上配置好测试环境(安装浏览器、驱动、Python环境),然后在构建步骤中执行测试命令。
一个简单的
.gitlab-ci.yml示例:stages: - test ui-automation-test: stage: test image: python:3.9-slim # 使用包含Python的Docker镜像 before_script: - apt-get update && apt-get install -y wget unzip # 安装Chrome浏览器 - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - - echo “deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main” >> /etc/apt/sources.list.d/google.list - apt-get update && apt-get install -y google-chrome-stable # 安装ChromeDriver(版本需匹配) - CHROME_VERSION=$(google-chrome --version | grep -oE ‘[0-9]+\.[0-9]+\.[0-9]+’) - wget -N https://chromedriver.storage.googleapis.com/$(curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE_${CHROME_VERSION%.*})/chromedriver_linux64.zip - unzip chromedriver_linux64.zip -d /usr/local/bin/ - chmod +x /usr/local/bin/chromedriver # 安装Python依赖 - pip install -r requirements.txt script: - pytest tests/ --alluredir=./allure-results # 运行测试并生成Allure结果 artifacts: when: always paths: - ./allure-results/ - ./screenshots/ expire_in: 1 week only: - main # 仅在main分支合并时触发 - merge_requests7.2 生成美观的测试报告
使用
pytest-html或Allure可以生成非常专业的测试报告。使用pytest-html(简单快捷):
pip install pytest-html pytest tests/ --html=report.html --self-contained-html这会生成一个独立的HTML报告文件,包含测试通过率、失败详情等。
使用Allure(功能强大、美观):
- 安装Allure命令行工具和pytest插件。
pip install allure-pytest # 还需在系统上安装Allure命令行,具体请参考Allure官网 - 在测试中增加Allure注解,增强报告可读性。
import allure import pytest @allure.feature(“登录功能”) class TestLogin: @allure.story(“用户使用正确凭证登录”) @allure.severity(allure.severity_level.CRITICAL) def test_login_success(self): with allure.step(“打开登录页面”): # ... 操作 pass with allure.step(“输入用户名和密码”): # ... 操作 pass with allure.step(“点击登录按钮”): # ... 操作 pass with allure.step(“验证登录成功”): # ... 断言 allure.attach(self.driver.get_screenshot_as_png(), name=“登录后页面”, attachment_type=allure.attachment_type.PNG) - 运行测试并生成报告。
pytest tests/ --alluredir=./allure-results allure serve ./allure-results # 本地生成并打开报告 # 或在CI中生成静态报告 allure generate ./allure-results -o ./allure-report --clean
Allure报告会展示清晰的特性、故事层级,步骤详情,并且可以方便地附加截图、日志,是团队分享测试结果的最佳选择。
学习Selenium的过程,就是一个不断将重复劳动转化为自动化脚本,再将零散脚本优化成可维护、可扩展的测试框架的过程。从最初录制回放,到熟练使用WebDriver API,再到应用Page Object、数据驱动等设计模式,最后集成到CI/CD流水线中,每一步都伴随着解决问题的成就感和效率提升的实在收益。记住,UI自动化测试不是要完全取代手工测试,而是将测试人员从高重复性的劳动中解放出来,让他们有更多精力去从事探索性测试、用户体验测试等更有价值的工作。开始编写你的第一个稳定、高效的Selenium测试脚本吧,你会发现,浏览器将从此听从你的代码指挥。
- 安装Allure命令行工具和pytest插件。
