1. 从零到一:为什么要在云上集成Selenium测试框架
做Web自动化测试的朋友应该都有类似的经历:本地脚本跑得好好的,一换环境就崩;领导要看报告,你得半夜爬起来截日志;项目组想搞持续集成,可测试机就那么一台,大家都在抢。这些问题归根结底是一件事——你的Selenium测试框架没有一个稳定、统一、可扩展的运行环境。
我这次把Selenium测试框架整体迁移整合到HoRain云上,就是冲着解决这些问题去的。先说说这个项目做了什么:把基于Selenium的Web自动化测试体系完整部署到云端环境中,包括Driver管理、浏览器运行时、测试执行节点、结果回传与报告展示全链路打通。简单来说,本地怎么跑,云端就怎么跑,而且能并发跑、定时跑、随时跑。
这篇攻略适合谁看呢?我按经验分了三类:
- 刚入门Selenium的新手,想搞清楚框架里各个组件是干什么的、怎么搭配;
- 已经在写脚本的测试开发,正被环境不一致、Driver失效、滑块验证码这类问题折磨;
- 负责测试基础设施的工程师,想把测试能力收拢到云端统一调度。
如果你属于任何一类,这篇内容都能给你一套可以直接落地的方案。我不会只贴代码,而是把每个环节的“为什么这么做”也讲清楚,包括我在实际部署中踩过的坑。
2. 整体设计:Selenium框架集成的核心思路
2.1 先搞清楚Selenium框架里到底有什么
很多人一上来就写脚本,其实Selenium测试框架并不是“写脚本”这么简单。它至少包含四层内容:
- 脚本层:用Java或Python写的测试用例,描述你要做什么操作、验证什么结果;
- 驱动层:ChromeDriver、GeckoDriver这类浏览器驱动,它们负责把Selenium命令翻译成浏览器能执行的操作;
- 运行时层:真正的浏览器实例(Chrome、Firefox)以及运行这些浏览器所需的操作系统环境;
- 调度层:任务怎么触发、测试跑在哪台机器上、结果怎么汇总。
本地开发的时候,这四层通常都在一台电脑上,问题不大。可一旦要上云、要持续集成、要多浏览器并行,驱动层和运行时层就会变成最大的不稳定因素。最常见的报错就是这个:
selenium.common.exceptions.WebDriverException: Message: unknown error: cannot find Chrome binary原因很简单:你本地装了Chrome,云端没装;或者云端装了Chrome,但ChromeDriver的版本跟Chrome版本对不上。所以框架集成的第一要务,是把驱动和运行时环境做标准化封装。
2.2 为什么选云环境而不是继续用本地机器
这个可能是很多人不太理解的地方:我本地跑得好好的,为什么非要折腾到云上去?我分享几个真实工作场景,你感受一下。
第一个场景,团队里有5个测试工程师,各写各的脚本,各跑各的浏览器。A用的Chrome 120,B用的Chrome 126,C的电脑上装的是Firefox。结果同一套用例,A跑通过、B跑失败,最后查了两天发现是浏览器版本差异导致的。这种问题在云端只用一个标准镜像,根本不会出现。
第二个场景,产品发版前要回归测试,300条用例本地串行跑要3个小时。你下午6点触发构建,晚上9点才能拿到结果,有问题还得第二天改。但云上可以开5个并发执行节点,把用例按模块拆分,半小时跑完,当晚就能修复。
第三个场景,临时要验证某个功能在老旧浏览器版本上的兼容性。本地装来装去麻烦不说,还容易把开发环境搞坏。云上开一个带旧版本Firefox的容器,测完销毁,干干净净。
这些不是极端需求,而是测试工作日常。所以我个人认为,Selenium框架上云不是“炫技”,而是把测试这件事变得可管理、可度量、可持续。
2.3 框架集成的三种主流方案对比
在确定技术路线之前,我梳理了一下当前业界主流的Selenium集成方案,各有各的适应场景。
| 方案 | 核心思路 | 优势 | 劣势 |
|---|---|---|---|
| 纯手动环境搭建 | 在每台执行机上手动装Python/Java、浏览器、Driver | 门槛低,容易理解 | 环境一致性差,扩展麻烦 |
| Docker容器化 | 把浏览器和Driver打进镜像,用容器作为执行环境 | 环境隔离好、扩展方便 | 需要掌握容器知识,调试稍复杂 |
| Selenium Grid分布式 | 用Hub和Node架构集中调度多浏览器节点 | 支持并发和分布式 | 配置和维护成本较高 |
这次在HoRain云上集成,我采用的是“Docker容器化 + Selenium Grid”的组合路线。每个浏览器节点是一个独立的Docker容器,节点与节点之间互不干扰;多个容器组成一个Grid集群,由Hub统一接收测试任务并分发到空闲节点。这样既解决了环境一致性问题,又拿到了并发执行能力,测试代码不用大改,性价比非常高。
3. 核心细节与实操要点
3.1 Python和Java两种接入方式怎么选
从相关热词就能看出来,现在用Python和Java接Selenium的人都不少。这俩语言各有拥趸,我给个比较中立的参考意见。
Python接入适合脚本逻辑轻、上手快、需要快速验证的场景。Python的Selenium库封装比较简洁,写起来很顺手。举个例子:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options options = Options() options.add_argument("--headless=new") # 无头模式,云端跑测试常用 driver = webdriver.Chrome(options=options) driver.get("https://example.com") element = driver.find_element(By.ID, "search-input") element.send_keys("HoRain云") driver.quit()优点很明显,代码量少,可读性好。但缺点也要说清楚:Python脚本发布之后依赖管理比较考验人,特别是你的脚本要在不同容器里跑的时候,pip依赖版本冲突是家常便饭。
Java接入则更适合中大型项目。Selenium官方对Java的支持最成熟,Maven/Gradle管理依赖非常清晰,再加上TestNG或JUnit的整合能力,在复杂业务场景下更稳。示例:
import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; public class QuickStart { public static void main(String[] args) { ChromeOptions options = new ChromeOptions(); options.addArguments("--headless=new"); WebDriver driver = new ChromeDriver(options); driver.get("https://example.com"); System.out.println(driver.getTitle()); driver.quit(); } }我的建议是:项目小、上手快选Python;项目大、多人协作、要长期维护选Java。这次云上部署我两套都做了,底层运行环境统一走Docker镜像,语言本身不冲突。
3.2 云上环境搭建的完整步骤
这部分我直接给一套可操作的流程,基于我在HoRain云上的操作经验。如果你用的是其他云平台或自建机房的Linux服务器,思路也大同小异。
第一步:准备基础镜像
我用的是Ubuntu 22.04作为基础镜像,安装Python 3.10、OpenJDK 11、Chrome浏览器以及对应的ChromeDriver。为什么选Ubuntu 22.04?因为它足够稳定,而且社区资料多,遇到问题好查。
FROM ubuntu:22.04 RUN apt-get update && apt-get install -y \ python3 python3-pip \ openjdk-11-jdk \ wget unzip xvfb # 安装Chrome RUN wget -q -O chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \ && dpkg -i chrome.deb || apt-get -f install -y \ && rm chrome.deb # 安装ChromeDriver(版本号必须与Chrome匹配) RUN CHROME_DRIVER_VERSION=$(curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE) \ && wget -q -O chromedriver.zip https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip \ && unzip chromedriver.zip -d /usr/local/bin \ && rm chromedriver.zip这里有个很重要的细节,就是代码里用到了LATEST_RELEASE这个接口获取最新版本号。实际生产环境中我不建议这么做,因为Chrome自动升级之后Driver版本可能又不匹配了。更稳的做法是固定一个Chrome版本,然后下载对应版本的Driver,镜像打上明确的版本标签。
第二步:安装并注册Selenium Manager
Selenium 4.6版本以后官方内置了Selenium Manager,它可以自动检测浏览器版本并下载匹配的Driver,这东西在云环境下特别友好。你不需要再手动管理Driver路径,只要把浏览器装好,Selenium会自动处理。
不过要注意,Selenium Manager在容器里可能需要额外的系统依赖,比如libnss3、libatk-bridge2.0-0等。缺了这些库,Chrome启动时会直接崩溃。安装基础依赖时我建议把这些一次性装全。
第三步:启用Xvfb虚拟显示
容器里没有物理显示器,Chrome要用无头模式运行,这个是常规做法。但有些场景下,比如你要截图做UI对比,或者要执行某些依赖渲染的JS操作,无头模式可能有兼容问题。这时候可以装Xvfb来做虚拟显示:
xvfb-run -a --server-args="-screen 0 1920x1080x24" python3 test_suite.py实操下来,Xvfb比--headless模式更接近真实浏览器行为,兼容性更好。代价是内存占用会大一些。如果你跑的是轻量级冒烟测试,无头模式足够;如果是全量回归,我建议用Xvfb。
3.3 Selenium Grid节点配置的避坑细节
Grid部署这块我遇到一个印象很深的坑。我配置了一个Chrome节点和一个Firefox节点,但测试任务大批量提交时,任务总往Chrome节点上堆积,Firefox节点闲着。排查了半天,定位到原因:节点标签配置不一致。
Selenium Grid 4支持用标签来标记节点能力,比如:
java -jar selenium-server-4.16.1.jar node --detect-drivers true --publish-events tcp://hub:4442 --subscribe-events tcp://hub:4443 --max-sessions 4但如果Hub上注册时明确定义了browserName=chrome,你就必须在测试代码中通过Capabilities显式指定浏览器类型,否则Hub会按照默认负载策略选择节点,容易造成分配不均。
解决办法是在脚本里加Capabilities配置:
ChromeOptions options = new ChromeOptions(); options.setCapability("browserName", "chrome"); options.setCapability("platformName", "linux"); options.setCapability("selenoid:options", Map.of("enableVNC", true, "enableVideo", false));另外一个细节是--max-sessions参数。默认每个节点只能跑1个会话,并发压不上去。要根据节点机器的CPU和内存合理设置,一般4核8G的机器开2-3个会话比较稳,开多了反而因为资源争抢导致用例超时。
4. 实操过程与核心环节实现
4.1 滑块验证自动化怎么处理
相关热词里出现频率极高的“网页拼图验证”“图片滑块验证”,这个是很多做自动化测试的人绕不开的坎。我在集成过程中也处理了这类场景,这里讲一下思路和代码实现。
滑块验证的自动化,本质上是三件事:找缺口、算距离、模拟拖动。先说找缺口,常用的方法有两种:
- 像素对比法:把带缺口的滑块背景图和完整背景图做像素级对比,找出差异区域的中心坐标;
- 边缘检测法:用OpenCV的Canny算子做边缘检测,再通过轮廓筛选定位缺口位置。
我实测下来,像素对比法在背景简单的情况下准确率高,但背景复杂时误判率上升。边缘检测法更稳定,推荐优先使用。示例代码用Python实现:
import cv2 import numpy as np def find_gap(full_img_path, gap_img_path): full = cv2.imread(full_img_path) gap = cv2.imread(gap_img_path) # 转为灰度图,减少计算量 full_gray = cv2.cvtColor(full, cv2.COLOR_BGR2GRAY) gap_gray = cv2.cvtColor(gap, cv2.COLOR_BGR2GRAY) # Canny边缘检测 full_edges = cv2.Canny(full_gray, 100, 200) gap_edges = cv2.Canny(gap_gray, 100, 200) # 模板匹配 result = cv2.matchTemplate(full_edges, gap_edges, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(result) if max_val < 0.5: return None # 没找到缺口,可能需要重试 return max_loc[0] + gap.shape[1] // 2 # 返回缺口中心X坐标算好距离之后,最关键的是模拟拖动。这里有个很多新手都会犯的错:用ActionChains直接把滑块从起点拖到终点,速度均匀、一步到位。这种操作轨迹太“完美”,很容易被网站的防自动化策略识别出来。
正确做法是模拟人手的操作特征:先慢后快再慢,中间带一点轻微抖动,甚至可以有少量回退。我封装了一个带轨迹模拟的拖动函数:
import random import time from selenium.webdriver.common.action_chains import ActionChains def drag_slider_with_human_track(driver, slider_element, distance): action = ActionChains(driver) action.click_and_hold(slider_element).perform() # 模拟人类拖动的加速度曲线 track = [] current = 0 mid = distance * 0.7 threshold = mid while current < distance: if current < threshold: move = random.randint(3, 8) # 加速阶段 else: move = random.randint(1, 4) # 减速阶段 current += move track.append(move) # 执行拖动 for move in track: action.move_by_offset(move, random.randint(-1, 1)).perform() time.sleep(random.uniform(0.01, 0.03)) # 结束前停顿一下,模拟人类确认位置 time.sleep(random.uniform(0.1, 0.2)) action.release().perform()这段代码实测下来通过率比一步到位高很多。但我要特别提醒:滑块验证自动化涉及网站的防自动化机制,适用范围应严格限定在你自己的测试环境、公司内网系统或已获授权的业务平台中。如果是外部网站,请先确认相关使用条款和法律法规,不要用于任何绕过安全机制的行为。
4.2 等待策略:隐式等待与显式等待的取舍
Selenium自动化测试里,元素加载慢、网络波动、页面异步渲染都是家常便饭。如果你不做合理的等待处理,脚本会频繁出现NoSuchElementException。很多新手的习惯是加time.sleep(5),简单粗暴但问题很大:等待时间太短不稳,太长拖慢整体执行速度。
正确的思路是组合使用显式等待和轮询机制。核心原则是:不要固定等多久,而是等到条件成立为止。
Python的WebDriverWait就是一个标准实现:
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 最多等10秒,每0.5秒检查一次元素是否可点击 element = WebDriverWait(driver, 10, poll_frequency=0.5).until( EC.element_to_be_clickable((By.ID, "submit-btn")) )Java对应的是FluentWait:
Wait<WebDriver> wait = new FluentWait<>(driver) .withTimeout(Duration.ofSeconds(10)) .pollingEvery(Duration.ofMillis(500)) .ignoring(NoSuchElementException.class); WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("submit-btn")));这里有一个我实践后总结的经验:等待时间不要一开始就设得很长。第一轮先设3秒,如果持续出现超时,再逐步加长,同时排查是不是页面本身有性能问题。等待不是万能的,如果某个元素经常要等10秒以上,那可能是前端性能问题,应该在缺陷跟踪系统里提bug,而不是默默在脚本里加超时时间。
4.3 文件下载如何控制时序
热词里有一条“Selenium怎样使文件下载完成之后才进行下一步”,这个场景很典型。做自动化测试时经常要从页面下载报表、图片、安装包,然后立刻校验文件内容。如果下载没完成就去读文件,大概率会读到不完整的文件。
我常用的方案是:配置浏览器的下载目录到指定文件夹,然后轮询检查文件是否存在,以及文件大小是否稳定。
import os import time def wait_for_download(download_dir, timeout=60): """ 等待下载完成,判断依据是文件名出现且文件大小在两次检查间不再变化 """ deadline = time.time() + timeout last_size = -1 stable_count = 0 while time.time() < deadline: files = [f for f in os.listdir(download_dir) if not f.endswith(".crdownload")] if files: file = os.path.join(download_dir, files[0]) current_size = os.path.getsize(file) if current_size == last_size: stable_count += 1 if stable_count >= 3: return os.path.join(download_dir, files[0]) else: last_size = current_size stable_count = 0 time.sleep(1) raise TimeoutError("Download timeout")这里有个细节,Chrome下载过程中会生成.crdownload临时文件,等你看到目标文件名且不再有.crdownload后缀时,才说明下载基本结束了。但单纯判断文件存在是不够的,因为文件刚创建时大小为0,你读到的还是空文件。所以我又加了一层“大小稳定”的判断,连续三次检查文件大小一致才算完成。
如果可以的话,更优雅的方案是走浏览器DevTools协议监听下载事件。Selenium 4支持通过Browser级别的命令来做:
driver.execute_cdp_cmd("Browser.setDownloadBehavior", { "behavior": "allow", "downloadPath": download_dir })这样下载行为完全可控,配合等待方法一起用,时序问题基本就解决了。
5. 常见问题与排查技巧实录
5.1 高频踩坑速查表
我把这次集成过程中遇到的高频问题整理成了一个速查表,方便你直接对照排查。
| 现象 | 根本原因 | 解决方案 |
|---|---|---|
WebDriverException: unknown error: cannot find Chrome binary | 容器里没装Chrome或Chrome路径未被识别 | 检查镜像是否安装Chrome,显式设置options.binary_location |
SessionNotCreatedException: This version of ChromeDriver only supports Chrome version XXX | ChromeDriver与Chrome版本不匹配 | 固定Chrome版本并下载对应Driver,或用Selenium Manager自动匹配 |
NoSuchElementException高频出现 | 等待策略不合理,元素还没渲染就去找 | 改用显式等待,等待元素可点击、可见等条件 |
ElementClickInterceptedException | 元素被弹窗或遮罩层挡住 | 先关闭遮罩层,或改用execute_script执行点击 |
| 浏览器启动后立刻崩溃 | 容器缺少系统库或渲染依赖 | 安装libnss3、libatk-bridge2.0-0等依赖,或用Xvfb虚拟显示 |
Grid节点一直显示Queue状态 | 节点配置的并发数超过机器承载能力 | 调小--max-sessions,检查CPU和内存使用率 |
下载文件一直以.crdownload结尾 | 下载未完成就执行了后续步骤 | 用等待方法轮询文件大小稳定后再继续 |
5.2 一个真实的调试案例
我在部署过程中遇到一个比较隐蔽的问题,花了一整天才定位到。现象是:本地执行用例全部通过,容器里执行却有约20%的用例随机失败,报错大多是ElementNotFound或TimeoutException。
一开始我以为是等待时间不够,把显式等待从5秒加到15秒,结果失败率基本没变。这就很奇怪了,如果只是加载慢,加时间应该有效果。后来我怀疑是资源问题,进容器里用htop看了一下,CPU使用率确实很高,但也没到100%。
最后我把目光放在浏览器进程数量上,发现问题来了:容器里配置了--max-sessions 4,但测试脚本用的是同一个Driver实例跑了多组数据。每次初始化Driver都会启动一个新的Chrome进程,4个并发会话加上各自内部可能产生的渲染进程,把容器内存挤爆了,导致部分页面加载时被强制回收。
解决方案有两个:一是调低节点的并发数到2,保证每个Chrome进程有足够内存;二是在脚本里增加更严格的资源释放逻辑,driver.quit()放在finally块里,确保用例结束就回收浏览器进程。
driver = None try: driver = webdriver.Chrome(options=options) # 执行测试用例... finally: if driver: driver.quit()这个案例给我的教训是:自动化测试框架的稳定性,不仅是脚本逻辑的问题,更是资源管理的问题。尤其是在容器环境里,内存和CPU都是受限资源,脚本写得好还要管得住资源生命周期。
5.3 日志与失败截图的最佳实践
最后说一说失败排查的底层能力——日志和截图。没有这两样东西,遇到问题就像闭着眼睛开车。我在框架里统一封装了失败截图逻辑,任何用例出错时自动截取当前页面,并保存DOM快照。
import traceback from datetime import datetime def screenshot_on_failure(driver, test_name): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") screenshot_path = f"reports/screenshots/{test_name}_{timestamp}.png" dom_path = f"reports/dom/{test_name}_{timestamp}.html" driver.save_screenshot(screenshot_path) with open(dom_path, "w", encoding="utf-8") as f: f.write(driver.page_source) return screenshot_path截图能告诉你页面上发生了什么,DOM快照能让你分析元素的真实状态。比如某个按钮点击无效,打开DOM快照可能发现按钮上有遮罩层,或者元素被disabled属性控制。这些信息在排障时极有价值,建议所有做Selenium自动化测试的团队都把这个能力集成到基建里。
日志这块,我建议分两级:运行日志和断言日志。运行日志记录每一步操作的耗时和结果,断言日志记录业务校验的具体数据。这样用例跑完后,你可以通过运行日志判断是环境问题还是脚本问题,通过断言日志判断是功能bug还是预期变更。
6. 集群并发与持续集成的进阶方案
6.1 在HoRain云上搭建并发执行节点
单节点跑测试再快也有上限,真正的效率提升来自并发。我在HoRain云上用Docker Compose快速拉起了一套Selenium Grid集群,核心配置是这样:
version: "3.8" services: selenium-hub: image: selenium/hub:4.16.1 container_name: selenium-hub ports: - "4442:4442" - "4443:4443" - "4444:4444" environment: - SE_SESSION_REQUEST_TIMEOUT=300 chrome-node-1: image: selenium/node-chrome:4.16.1 depends_on: - selenium-hub environment: - SE_EVENT_BUS_PUBLISH=4442 - SE_EVENT_BUS_SUBSCRIBE=4443 - SE_NODE_MAX_SESSIONS=3 volumes: - /dev/shm:/dev/shm chrome-node-2: image: selenium/node-chrome:4.16.1 depends_on: - selenium-hub environment: - SE_EVENT_BUS_PUBLISH=4442 - SE_EVENT_BUS_SUBSCRIBE=4443 - SE_NODE_MAX_SESSIONS=3 volumes: - /dev/shm:/dev/shm firefox-node-1: image: selenium/node-firefox:4.16.1 depends_on: - selenium-hub environment: - SE_EVENT_BUS_PUBLISH=4442 - SE_EVENT_BUS_SUBSCRIBE=4443 - SE_NODE_MAX_SESSIONS=2 volumes: - /dev/shm:/dev/shm注意/dev/shm这个volume挂载,这是官方镜像都特别强调的一个点。容器默认的/dev/shm只有64MB,浏览器渲染时会把这个空间占满,导致崩溃或异常。挂载到宿主机的共享内存后,问题就解决了。
6.2 测试用例怎么拆才能并发
不是所有用例都适合丢到并发里去跑。如果你把一个流程式的测试放到多个节点上并发跑,可能因为共享状态产生数据冲突。我习惯把测试用例分成三类:
- 冒烟用例:核心链路,必须跑通,串行执行,10分钟内出结果;
- 业务回归用例:模块内独立性强,可以按功能模块拆到不同节点并发执行;
- 数据一致性用例:涉及公共数据变更,统一放到最后串行跑。
并发执行时还有一个关键点:用例之间不要共享数据。比如用例A创建了一个订单,用例B去修改这个订单,这俩一旦并发就有竞态问题。正确做法是每条用例自己创建数据、自己清理数据,保证数据隔离。
6.3 与CI/CD流水线的对接
测试框架搭好了要接进流水线才有价值。我把Selenium测试任务挂到了CI流水线的指定阶段,代码合并后自动触发。核心思路是:
- CI构建产物发布到测试环境,触发测试任务;
- 测试任务用Docker Compose拉起Grid集群;
- 测试用例容器连接Grid执行用例;
- 用例结束后生成测试报告并上传到内部平台;
- 最后销毁整个Grid集群。
这样整个测试过程完全自动化,不再需要人工干预。我在实践中体会特别深的一点是:测试环境用完要销毁。很多人图省事,Grid集群常年挂着跑,结果版本迭代后镜像里的浏览器版本老得不能再老,用例随机失败频发。用Docker Compose管理的好处就在这里,测试完一条命令销毁所有资源,下次跑的时候重新拉起,保证每次用的都是最新镜像。
7. 写在最后的一些实际经验
集成过程中踩了不少坑,最后分享几个我个人觉得最有价值的小经验。
第一个是关于浏览器版本的。永远不要在你的镜像或者环境里使用"最新版"指望它一直稳定。我遇到过几次Chrome自动更新之后ChromeDriver不匹配,所有用例全部挂掉的情况。现在我的做法是固定Chrome版本,镜像打上明确标签,比如chrome:120.0.6099.109。需要升级时,先在一个测试节点上升级验证,确认没有问题再打新镜像。
第二个是关于等待时间的。能不用sleep就不用,能局部等待就不要全局等待。一个300条用例的回归套件,如果每条用例多了3秒的无效等待,整体就要多跑15分钟。积少成多,这个账一定要算。
第三个是关于日志的。测试框架的日志一定要结构化,至少包含时间戳、用例名称、操作步骤、耗时。这样出了问题定位快,你不需要登录到服务器上看,通过日志索引就能判断是环境问题还是脚本问题,还是业务bug。
第四个是关于云上资源规划的。Selenium测试容器吃内存很厉害,一个Chrome实例加渲染进程大约要500MB到1GB内存。你在规划并发数的时候,先算出机器总内存,再除以每个实例的内存预估,留出30%的余量,这样才是合理的并发配置。靠感觉拍脑袋设并发数,很容易在上线后遭遇OOM。
这次在HoRain云上做Selenium框架集成的整体收获,我觉得不只是跑通了自动化测试,更重要的是把测试环境从"个人电脑里的黑盒"变成了"团队可管理、可扩展的标准服务"。当你发现新增10个用例只是改一行并行配置,当你在凌晨收到CI失败通知却能在10分钟内定位问题,你会觉得之前那些折腾都是值得的。