Selenium自动化测试实战:从环境搭建到框架设计与CI/CD集成

📅 2026/6/26 20:50:38
Selenium自动化测试实战:从环境搭建到框架设计与CI/CD集成
1. 项目概述为什么我们需要让浏览器“听话”如果你是一名测试工程师、开发人员或者任何需要和网页打交道的角色下面这个场景你一定不陌生每天上班打开电脑登录系统点击菜单输入数据提交表单检查结果……一套流程下来半小时过去了而这样的操作一天可能要重复几十次。更头疼的是当产品更新、需求变更时你又要重新手动走一遍所有流程确保新功能没把老功能搞坏。这种重复、枯燥、易错的工作不仅消耗大量时间也消磨人的热情。这就是为什么我们需要“自动化测试”而Selenium正是让这一切成为可能的“神器”。简单来说Selenium是一个用于Web应用程序自动化测试的强大工具集。它允许你编写脚本模拟真实用户的操作——打开浏览器、输入网址、点击按钮、填写表单、抓取数据、验证结果。你可以把它想象成一个不知疲倦、不会犯错、且能严格按照你指令行事的“数字员工”。它的核心价值在于将我们从重复的、机械的Web操作中解放出来把精力投入到更有创造性的工作中比如设计更复杂的测试场景、分析测试结果、或者优化产品逻辑。Selenium之所以被称为“神器”不仅因为它免费、开源、社区庞大更因为它支持几乎所有主流浏览器Chrome、Firefox、Edge、Safari等和多种编程语言Python、Java、C#、JavaScript等。无论你是前端开发者想验证页面交互后端工程师想确保接口改动不影响页面还是测试专员需要构建完整的回归测试套件Selenium都能提供一套统一的解决方案。它让“浏览器乖乖听话”从一句玩笑变成了可以落地的工程实践。接下来我将从一个有十多年经验的实践者角度带你从零开始深入Selenium的每一个核心环节分享那些官方文档里不会写的“坑”和“技巧”。2. 环境搭建与核心组件解析在开始写第一行自动化脚本之前搭建一个稳定、可复现的环境是成功的第一步。很多新手在这里就会遇到各种“玄学”问题比如脚本跑不起来、浏览器打不开、或者运行一次就报错。其根本原因是对Selenium的架构和依赖关系理解不透彻。我们来彻底拆解一下。2.1 Selenium架构三位一体的协作模式很多人以为装个selenium库就能用了其实远不止如此。一个完整的Selenium自动化环境通常由三部分组成它们各司其职缺一不可Selenium Client Libraries客户端库这就是我们常说的selenium包比如Python的selenium。它提供了一套友好的API例如find_element,click,send_keys让我们能用熟悉的编程语言编写测试脚本。它本身并不直接操作浏览器。WebDriver这是整个架构的核心与桥梁也是新手最容易出错的地方。WebDriver是一个独立的可执行程序如chromedriver.exe,geckodriver.exe由各个浏览器厂商Google, Mozilla提供并维护。它的作用是接收来自Client Libraries的指令通过HTTP协议并将其翻译成浏览器能理解的原生操作。重要提示WebDriver的版本必须与你的本地浏览器版本严格匹配版本不匹配是导致“浏览器打不开”或“莫名报错”的头号原因。浏览器Browser真正的执行终端如Chrome、Firefox。WebDriver通过浏览器提供的调试接口如Chrome DevTools Protocol来控制它。它们三者的关系好比一次远程操控你Client Libraries用对讲机发出指令“点击登录按钮”通讯员WebDriver听到后跑到机器Browser面前亲手按下那个按钮。如果通讯员听不懂你的语言库版本太旧或者机器型号和通讯员会的操作手册对不上浏览器与WebDriver版本不匹配指令就无法执行。2.2 手把手搭建Python Chrome环境我们以最流行的组合“Python Chrome”为例演示如何一步步搭建一个“坚如磐石”的环境。第一步安装Python与Selenium库确保你已安装Python建议3.7以上版本。通过pip安装Selenium库这是最稳定可靠的方式。pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple使用国内镜像源如清华源可以大幅加快下载速度。第二步确定Chrome浏览器版本打开你的Chrome浏览器点击右上角三个点 - 帮助 - 关于Google Chrome。记下完整的版本号例如版本 128.0.6613.138正式版本 64 位。核心版本号是128.0.6613.138。第三步下载匹配的ChromeDriver这是最关键的一步。前往ChromeDriver的官方下载站点搜索“ChromeDriver”即可找到。你会看到一系列版本号。你的选择原则是ChromeDriver的主版本号必须与你的Chrome浏览器主版本号完全一致。对于上面的例子主版本号是128那么你就应该下载标注为ChromeDriver 128.x.x.x的版本小版本号可以忽略但主版本必须一致。选择对应你操作系统的文件Windows选chromedriver_win32.zipMac选chromedriver_mac64.zipLinux选chromedriver_linux64.zip。注意绝对不要使用一些第三方网站提供的“万能驱动”或“最新版驱动”。浏览器频繁更新只有从官方渠道下载的对应版本才能保证最大的兼容性和稳定性。我曾因为图省事用了版本号差1的驱动导致脚本在团队其他成员的机器上全部失败排查了半天。第四步配置WebDriver路径下载的ZIP包解压后你会得到一个名为chromedriverWindows下为chromedriver.exe的可执行文件。你有三种方式让Python脚本找到它方法A推荐项目内管理将chromedriver.exe文件直接放在你的Python项目根目录下。Selenium默认会在系统PATH和当前目录下查找。方法B系统路径将chromedriver.exe所在目录添加到系统的环境变量PATH中。这样在任何地方都能调用。方法C代码指定在代码中显式指定驱动路径。from selenium import webdriver from selenium.webdriver.chrome.service import Service # 指定chromedriver的绝对路径 service Service(executable_pathrC:\path\to\your\chromedriver.exe) driver webdriver.Chrome(serviceservice)我强烈推荐方法A尤其是在团队协作中。将驱动文件和脚本一起提交到代码仓库可以确保所有成员的环境完全一致避免“在我机器上是好的”这类问题。2.3 验证环境写下你的第一个自动化脚本创建一个名为first_test.py的文件输入以下代码from selenium import webdriver from selenium.webdriver.common.by import By import time # 1. 创建浏览器驱动实例如果chromedriver在当前目录无需指定路径 driver webdriver.Chrome() # 2. 打开百度首页 driver.get(https://www.baidu.com) # 3. 等待页面加载一下 time.sleep(2) # 4. 找到搜索框输入“Selenium” search_box driver.find_element(By.ID, kw) # 百度搜索框的ID是kw search_box.send_keys(Selenium) # 5. 找到“百度一下”按钮并点击 search_button driver.find_element(By.ID, su) search_button.click() # 6. 等待搜索结果加载 time.sleep(3) # 7. 在控制台打印当前页面标题 print(当前页面标题是, driver.title) # 8. 关闭浏览器 driver.quit()运行这个脚本。如果一切顺利你会看到Chrome浏览器自动打开访问百度输入关键词并搜索最后在控制台打印出标题然后浏览器关闭。恭喜你你的“数字员工”已经成功上岗了实操心得在初期使用time.sleep(seconds)进行固定等待虽然简单但并不是最佳实践。因为它会造成不必要的时间浪费如果页面提前加载好了或者等待不足如果网络慢。在后续章节我们会用更智能的“显式等待”来替代它。但无论如何第一个能跑起来的脚本带来的正反馈是坚持下去的最大动力。3. Selenium核心操作与定位策略详解让浏览器动起来只是第一步如何精准地找到页面上的元素并与之交互才是自动化脚本稳定性的基石。这一章我们将深入Selenium最核心的部分元素定位与操作。3.1 八种元素定位器你该用哪种Selenium提供了8种定位元素的方法对应By类的不同属性。选择正确的定位器就像给一个地址越精确越好找。ID (By.ID)通过元素的id属性定位。id在HTML中理论上应该是唯一的是首选定位方式速度快最稳定。element driver.find_element(By.ID, “username”)Name (By.NAME)通过元素的name属性定位。常用于表单元素如输入框、单选按钮。element driver.find_element(By.NAME, “password”)Class Name (By.CLASS_NAME)通过元素的class属性定位。注意一个元素可能有多个class用空格分隔这里匹配的是完整的class字符串。element driver.find_element(By.CLASS_NAME, “btn-submit”)Tag Name (By.TAG_NAME)通过标签名定位如input,div,a。通常一个页面有很多相同标签所以常与find_elements复数结合使用获取列表后再筛选。all_links driver.find_elements(By.TAG_NAME, “a”)Link Text (By.LINK_TEXT)精确匹配a标签的完整文本内容。用于定位超链接。element driver.find_element(By.LINK_TEXT, “忘记密码”)Partial Link Text (By.PARTIAL_LINK_TEXT)匹配a标签文本的部分内容。比Link Text更灵活。element driver.find_element(By.PARTIAL_LINK_TEXT, “忘记”) # 也能找到“忘记密码”CSS Selector (By.CSS_SELECTOR)使用CSS选择器语法定位功能非常强大且灵活是第二推荐的定位方式。可以组合ID、Class、属性、层级关系等。# 定位id为‘kw’的元素 element driver.find_element(By.CSS_SELECTOR, “#kw”) # 定位class包含‘btn’且type为‘submit’的按钮 element driver.find_element(By.CSS_SELECTOR, “.btn[type‘submit’]”)XPath (By.XPATH)使用XML路径语言定位功能最强大几乎可以定位任何元素但语法相对复杂执行速度可能稍慢于CSS Selector。当其他定位器都失效时XPath是最后的杀手锏。# 绝对路径脆弱不推荐 element driver.find_element(By.XPATH, “/html/body/div[1]/form/input[1]”) # 相对路径属性推荐 element driver.find_element(By.XPATH, “//input[id‘kw’]”) # 文本内容定位 element driver.find_element(By.XPATH, “//button[text()‘登录’]”)定位策略优先级建议首选ID唯一且快速。次选CSS Selector灵活、性能好现代前端框架如Vue、React生成的元素可能没有稳定ID但CSS选择器通常能稳定定位。谨慎使用XPath避免使用包含索引如div[1]或过长路径的绝对XPath因为它们极易因页面结构微小变动而失效。尽量使用基于ID、属性或文本的相对XPath。避免使用Class Name定位单一元素除非你确信该class唯一否则页面可能有多个元素共享同一个class。Link Text系列仅用于链接很直观。踩坑实录早期我大量使用浏览器开发者工具直接复制的XPath这些XPath往往是包含大量div、table索引的绝对路径。一次前端重构调整了几个div的顺序整个自动化测试套件全部崩溃。教训就是定位器必须具有“鲁棒性”。尽量使用不会轻易改变的属性如业务相关的id、name或者具有特殊意义的>from selenium.webdriver.support.ui import Select select_element Select(driver.find_element(By.ID, “country”)) select_element.select_by_visible_text(“中国”) # 按文本选择 select_element.select_by_value(“CN”) # 按value属性选择 select_element.select_by_index(1) # 按索引选择从0开始鼠标悬停ActionChains用于触发下拉菜单等需要悬停的事件。from selenium.webdriver.common.action_chains import ActionChains menu driver.find_element(By.ID, “dropdownMenu”) ActionChains(driver).move_to_element(menu).perform()文件上传对于input type“file”元素直接使用send_keys传入文件本地绝对路径即可不要尝试模拟点击“选择文件”按钮的复杂操作。driver.find_element(By.ID, “fileInput”).send_keys(r“C:\Users\test\image.png”)3.3 等待的艺术告别time.sleep这是区分新手和老手的关键。硬编码的time.sleep是万恶之源。隐式等待Implicit Wait设置一个全局的超时时间在查找元素时如果元素没有立即出现WebDriver会轮询DOM直到找到它或超时。driver.implicitly_wait(10) # 单位秒注意它只对find_element和find_elements方法生效。设置一次对整个driver生命周期都有效。但它无法处理更复杂的条件比如元素可点击。显式等待Explicit Wait这是工业级自动化测试的标配。它允许你为某个特定条件设置等待条件成立则继续超时则抛出异常。需要配合WebDriverWait和expected_conditionsEC使用。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待ID为‘result’的元素出现最多等10秒每0.5秒检查一次 element WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, “result”)) ) # 等待元素可点击 button WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, “submitBtn”)) ) button.click()常用预期条件ECpresence_of_element_located: 元素出现在DOM中不一定可见。visibility_of_element_located: 元素可见。element_to_be_clickable: 元素可见且可点击。text_to_be_present_in_element: 元素中包含特定文本。最佳实践混合使用以显式等待为主可以设置一个较短的隐式等待如5秒作为兜底然后针对关键操作使用更精确的显式等待。为不同的操作设置不同的超时时间登录按钮可以等10秒一个加载动画可能只需要等3秒。永远避免使用固定的time.sleep除非是等待一个与网络或DOM无关的固定动画极少情况。4. 构建健壮的自动化测试框架当脚本越来越多你会发现直接写一堆线性脚本会变得难以维护。这时就需要引入一些设计和模式构建一个可维护、可扩展的测试框架。这不是必须的但对于任何严肃的自动化项目来说都是迟早要走的一步。4.1 Page Object Model (POM)页面对象模型POM是Selenium自动化测试中最经典、最重要的设计模式。其核心思想是将测试脚本业务逻辑与页面元素定位和操作细节分离。没有POM的代码难以维护def test_login(): driver.find_element(By.ID, “username”).send_keys(“admin”) driver.find_element(By.ID, “password”).send_keys(“123456”) driver.find_element(By.ID, “submit”).click() # ... 断言代码散落各处使用POM后的代码创建页面对象类如login_page.pyfrom 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) # 定位器 USERNAME_INPUT (By.ID, “username”) PASSWORD_INPUT (By.ID, “password”) SUBMIT_BUTTON (By.ID, “submit”) ERROR_MSG (By.CLASS_NAME, “error-message”) # 页面操作方法 def enter_username(self, username): self.wait.until(EC.visibility_of_element_located(self.USERNAME_INPUT)).send_keys(username) return self # 支持链式调用 def enter_password(self, password): self.driver.find_element(*self.PASSWORD_INPUT).send_keys(password) return self def click_submit(self): self.driver.find_element(*self.SUBMIT_BUTTON).click() def get_error_message(self): try: return self.driver.find_element(*self.ERROR_MSG).text except: return None在测试脚本中使用如test_login.pyimport pytest from login_page import LoginPage def test_login_success(driver): # 假设driver通过fixture提供 login_page LoginPage(driver) login_page.enter_username(“admin”).enter_password(“123456”).click_submit() # 跳转到主页进行后续断言... def test_login_failed(driver): login_page LoginPage(driver) login_page.enter_username(“wrong”).enter_password(“wrong”).click_submit() error_msg login_page.get_error_message() assert “用户名或密码错误” in error_msgPOM的优势高可维护性当页面元素ID变化时你只需要在一个地方页面对象类修改定位器所有测试用例自动生效。高可读性测试用例读起来就像自然语言清晰地描述了“做什么”而不是“怎么做”。低冗余公共操作被封装避免重复代码。团队协作测试开发人员负责维护页面对象业务测试人员可以专注于编写测试用例逻辑。4.2 数据驱动测试将测试数据如用户名、密码、搜索关键词从测试脚本中分离出来存储在外部的文件如JSON、YAML、Excel、CSV或数据库中。这样同一套测试逻辑可以用多组数据进行验证。使用pytest和pytest.mark.parametrize实现数据驱动import pytest from login_page import LoginPage # 测试数据列表每个元组是一组测试数据 test_data [ (“admin”, “123456”, True, “登录成功”), (“”, “123456”, False, “用户名不能为空”), (“admin”, “”, False, “密码不能为空”), (“wrong”, “wrong”, False, “用户名或密码错误”), ] pytest.mark.parametrize(“username, password, expected_success, expected_msg”, test_data) def test_login_with_data(driver, username, password, expected_success, expected_msg): login_page LoginPage(driver) login_page.enter_username(username).enter_password(password).click_submit() if expected_success: # 验证登录成功例如跳转到首页 assert “首页” in driver.title else: # 验证登录失败提示信息正确 actual_msg login_page.get_error_message() assert expected_msg in actual_msg运行一次pytest会自动生成并运行4个独立的测试用例。这极大地提高了测试覆盖率并且添加新测试数据非常方便。4.3 测试报告与日志自动化测试如果不产生清晰的报告就失去了大半价值。你需要知道哪些用例通过了哪些失败了失败的原因是什么。使用pytest内置报告pytest运行后会自动生成简洁的控制台报告。使用-v参数可以查看更多细节。生成HTML报告使用pytest-html插件可以生成美观的HTML报告。pip install pytest-html pytest --htmlreport.html --self-contained-html集成Allure报告Allure能生成非常专业、交互性强的测试报告支持图表、附件截图、步骤描述等是很多大型项目的选择。配置稍复杂但效果出众。日志记录在框架中集成Python的logging模块记录脚本执行的关键步骤和错误信息便于离线排查。import logging logging.basicConfig(levellogging.INFO, format‘%(asctime)s - %(levelname)s - %(message)s’) def click_element(element, element_name): try: element.click() logging.info(f“成功点击元素: {element_name}”) except Exception as e: logging.error(f“点击元素 {element_name} 失败: {e}”) raise4.4 用例组织与执行控制使用pytest标记Mark可以对测试用例进行分类例如pytest.mark.smoke冒烟测试、pytest.mark.regression回归测试。然后可以只运行特定标记的用例。pytest -m smoke # 只运行冒烟测试 pytest -m “not slow” # 运行除了标记为slow的所有测试Fixture夹具pytest的Fixture用于提供测试依赖如初始化driver、登录、清理数据可以实现用例级别的setup和teardown是管理测试生命周期的最佳实践。import pytest pytest.fixture(scope“function”) # 每个测试函数执行一次 def driver(): # 初始化driver d webdriver.Chrome() d.implicitly_wait(5) yield d # 将driver提供给测试用例 # 测试结束后清理 d.quit() pytest.fixture def login(driver): # 封装登录操作供需要登录状态的用例使用 page LoginPage(driver) page.enter_username(“admin”).enter_password(“123456”).click_submit() yield # 登录状态已就绪 # 如果需要可以在这里做登出清理5. 高级技巧与疑难问题排查当基础功能都掌握后你会遇到一些更棘手的挑战。这一章分享一些高级技巧和常见“坑”的解决方案。5.1 处理弹窗、Alert和多个窗口JavaScript Alert/Confirm/Prompt使用driver.switch_to.alert。alert driver.switch_to.alert print(alert.text) # 获取提示文本 alert.accept() # 点击“确定” # alert.dismiss() # 点击“取消” # alert.send_keys(“input text”) # 用于Prompt弹窗输入浏览器弹窗非JS Alert有些弹窗是div层模拟的需要像普通元素一样定位和操作。多窗口/标签页# 获取当前所有窗口句柄 all_handles driver.window_handles # 获取当前窗口句柄 current_handle driver.current_window_handles # 切换到新窗口 for handle in all_handles: if handle ! current_handle: driver.switch_to.window(handle) break # 操作新窗口... # 切回原窗口 driver.switch_to.window(current_handle)5.2 执行JavaScript代码有些操作通过WebDriver API难以实现比如滚动到页面底部、修改元素属性、执行复杂的DOM操作。这时可以直接注入JavaScript。# 滚动到页面底部 driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) # 滚动到元素可见 element driver.find_element(By.ID, “footer”) driver.execute_script(“arguments[0].scrollIntoView(true);”, element) # 修改元素属性例如让一个隐藏的输入框可见 driver.execute_script(“document.getElementById(‘hiddenInput’).style.display ‘block’;”) # 获取页面标题示例通常用driver.title即可 title driver.execute_script(“return document.title;”)5.3 文件下载与上传文件下载需要设置浏览器选项指定下载路径并禁用下载弹窗。from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options Options() prefs { “download.default_directory”: r“C:\Downloads”, # 设置下载路径 “download.prompt_for_download”: False, # 禁用下载提示 “plugins.always_open_pdf_externally”: True # 总是直接下载PDF等 } chrome_options.add_experimental_option(“prefs”, prefs) driver webdriver.Chrome(optionschrome_options)文件上传如前所述对于input type“file”直接用send_keys传路径。5.4 绕过登录与处理Cookie在测试需要登录的页面时反复走登录流程既慢又不稳定。更高效的方式是手动登录一次获取登录后的Cookie。在自动化脚本中直接添加这些Cookie然后访问目标页面实现“免登录”状态。# 假设你已经手动登录并保存了cookie字典列表 cookies [ {‘name’: ‘sessionid’, ‘value’: ‘abc123...’, ‘domain’: ‘.yourdomain.com’}, # ... 其他cookie ] driver.get(“https://www.yourdomain.com/”) # 先访问域名才能设置该域名的cookie for cookie in cookies: driver.add_cookie(cookie) # 再次访问需要登录的页面此时已处于登录状态 driver.get(“https://www.yourdomain.com/user/profile”)注意Cookie有有效期且可能绑定IP或浏览器指纹。此方法更适合测试环境或短期内的回归测试。5.5 常见疑难问题排查清单即使经验丰富也会遇到各种奇怪的问题。下面是一个快速排查清单问题现象可能原因排查步骤与解决方案浏览器无法启动报错找不到驱动1. ChromeDriver未在PATH中或路径错误。2. ChromeDriver与Chrome浏览器版本不匹配。1. 检查代码中指定的驱动路径或将其放入系统PATH。2.核对并确保主版本号完全一致。去官网下载对应版本。元素找不到NoSuchElementException1. 定位器写错了。2. 元素尚未加载出来。3. 元素在iframe或shadow DOM内。4. 页面有动态ID或类名。1. 用浏览器开发者工具F12的Console输入$x(‘your_xpath’)或$$(‘your_css’)验证定位器。2.使用显式等待WebDriverWait。3. 切换到对应的iframedriver.switch_to.frame(frame_element)。4. 使用更稳定的定位策略如XPath文本、CSS属性选择器。元素不可交互ElementNotInteractableException1. 元素被遮挡如弹窗、其他div。2. 元素不可见display:none或visibility:hidden。3. 元素未处于可点击状态如禁用按钮。1. 等待遮挡物消失或滚动元素到视图内scrollIntoView。2. 检查元素样式或等待其变为可见。3. 等待element_to_be_clickable条件成立。脚本被网站检测为自动化工具一些网站如某些登录、滑块验证页面会检测WebDriver特征。1. 添加实验性选项排除自动化特征仅对部分网站有效chrome_options.add_experimental_option(“excludeSwitches”, [“enable-automation”])chrome_options.add_experimental_option(‘useAutomationExtension’, False)2. 使用undetected-chromedriver等第三方库高级用法。3. 对于核心业务考虑与开发沟通在测试环境关闭相关检测。执行速度慢1. 大量使用time.sleep。2. 网络或应用本身响应慢。3. 使用了效率低的定位器如复杂XPath。1.全面替换为显式等待。2. 优化测试环境网络或适当增加等待超时时间。3. 优化定位器优先使用ID和CSS Selector。测试结果不稳定Flaky Tests1. 依赖网络或第三方服务不稳定。2. 异步加载内容未完全就绪。3. 测试数据被污染或依赖顺序。1. 对不稳定操作增加重试机制可使用pytest的pytest.mark.flaky或自己写重试逻辑。2. 等待更全面的页面加载条件如某个特定元素出现、Ajax请求完成。3. 保证测试用例的独立性每个用例前后做好数据清理和恢复使用Fixture。6. 持续集成与实战建议将自动化测试集成到持续集成/持续部署CI/CD流水线中是实现其最大价值的关键一步。同时分享一些我多年实践下来的“生存法则”。6.1 集成到CI/CD以Jenkins为例目标每次代码提交后自动触发自动化测试套件执行并生成测试报告。准备测试脚本与依赖确保你的项目有一个requirements.txt文件列出所有Python依赖包括selenium,pytest,pytest-html等。编写执行脚本创建一个Shell脚本如run_tests.sh或批处理文件用于在无界面的CI服务器上执行测试。# run_tests.sh (Linux示例) #!/bin/bash # 安装依赖 pip install -r requirements.txt # 下载对应版本的ChromeDriver如果CI环境没有预装 # 此处省略下载逻辑建议将驱动预置在项目中或使用Docker镜像 # 运行测试并生成报告 pytest --htmlreport.html --self-contained-html -v配置无头模式HeadlessCI服务器通常没有图形界面需要在代码中配置浏览器以无头模式运行。from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options Options() chrome_options.add_argument(“--headless”) # 启用无头模式 chrome_options.add_argument(“--no-sandbox”) # 在CI环境中常需要 chrome_options.add_argument(“--disable-dev-shm-usage”) # 解决共享内存问题 driver webdriver.Chrome(optionschrome_options)在Jenkins中创建任务新建一个“自由风格”项目。在“源码管理”中配置你的代码仓库Git。在“构建触发器”中设置触发规则如轮询SCM或GitHub hook。在“构建”步骤中选择“执行Shell”或“执行Windows批处理命令”调用你的run_tests.sh。在“后构建操作”中添加“Publish HTML reports”插件将生成的report.html发布到Jenkins界面方便查看。6.2 给自动化测试新手的十条建议从小处着手快速验证不要一开始就想自动化整个系统。挑选一个最核心、最重复的流程如用户登录开始快速做出一个可运行的脚本获得信心和反馈。定位器是生命线花时间研究并写出健壮的定位器。与前端开发沟通为关键测试元素添加稳定的id或>