3. 浏览器驱动封装:WebDriver单例模式实现、多浏览器支持与选项配置
好,咱们进入第三个实战环节。浏览器驱动封装,这名字听着挺唬人,说白了就是给 WebDriver 找个靠谱的“管家”。
我刚开始带团队的时候,发现一个特别头疼的问题:测试用例跑着跑着,浏览器窗口开了十几个,内存直接爆掉。后来一查,原来是每个用例都 new 了一个 WebDriver 实例,用完还不关。嗯,这就是典型的“驱动管理混乱症”。
今天咱们就彻底解决它。核心就三件事:单例模式保证唯一实例、多浏览器一键切换、浏览器选项灵活配置。
3.1 为什么需要单例模式?
你想想看,一个自动化测试工程,可能有几百个测试用例。如果每个用例都自己创建一个浏览器驱动,那会怎样?
- 资源浪费:每个实例都占用几百 MB 内存
- 状态混乱:多个窗口互相干扰,定位元素经常失败
- 维护成本高:改一个浏览器配置,要改几十个地方
单例模式,就是保证整个测试生命周期里,只有一个 WebDriver 实例。谁要用,都来找这个唯一的实例。用完也不销毁,等所有用例跑完,统一关闭。
核心原则:一个测试进程,一个浏览器实例。别搞“一人一窗口”那套。
3.2 单例模式实现:Java 版本
我个人习惯用 Java 写自动化框架,因为生态成熟。咱们直接看代码,我加了很多注释,方便你理解。
public class WebDriverSingleton {
// 1. 私有静态变量,保存唯一实例
private static WebDriver driver = null;
// 2. 私有构造方法,防止外部 new
private WebDriverSingleton() {
// 啥也不干,就是不让 new
}
// 3. 公共静态方法,获取唯一实例
public static WebDriver getDriver() {
if (driver == null) {
// 第一次调用时,才创建实例
// 这里可以读取配置文件,决定用哪个浏览器
String browser = ConfigReader.getBrowser(); // Chrome/Firefox/Edge
driver = createDriver(browser);
}
return driver;
}
// 4. 创建具体浏览器驱动
private static WebDriver createDriver(String browser) {
WebDriver webDriver;
switch (browser.toLowerCase()) {
case "chrome":
webDriver = createChromeDriver();
break;
case "firefox":
webDriver = createFirefoxDriver();
break;
case "edge":
webDriver = createEdgeDriver();
break;
default:
throw new RuntimeException("不支持的浏览器类型: " + browser);
}
// 设置隐式等待,全局生效
webDriver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
// 窗口最大化
webDriver.manage().window().maximize();
return webDriver;
}
// 5. 关闭驱动
public static void quitDriver() {
if (driver != null) {
driver.quit();
driver = null; // 置空,方便下次重新创建
}
}
}
避坑指南:我曾经犯过一个低级错误——在 quitDriver() 里忘了把 driver 置为 null。结果第二次调用 getDriver() 时,判断 driver != null,直接返回了一个已经关闭的实例,所有操作都报错。嗯,这个坑我替你们踩过了。
3.3 多浏览器支持:Chrome / Firefox / Edge
现在的项目,往往要兼容多个浏览器。你不能写死成 Chrome,万一客户用 Edge 呢?
我的做法是:通过配置文件控制浏览器类型。测试人员改一行配置,就能切换浏览器,不用改任何代码。
来看具体的创建方法:
3.3.1 Chrome 驱动创建
private static WebDriver createChromeDriver() {
ChromeOptions options = new ChromeOptions();
// 基础配置
options.addArguments("--disable-notifications"); // 禁用通知弹窗
options.addArguments("--disable-popup-blocking"); // 禁用弹窗拦截
options.addArguments("--lang=zh-CN"); // 设置中文语言
// 读取配置,决定是否启用无头模式
if (ConfigReader.isHeadless()) {
options.addArguments("--headless"); // 无头模式
options.addArguments("--disable-gpu"); // 禁用 GPU 加速
options.addArguments("--no-sandbox"); // 禁用沙盒(Linux 环境必需)
}
return new ChromeDriver(options);
}
3.3.2 Firefox 驱动创建
private static WebDriver createFirefoxDriver() {
FirefoxOptions options = new FirefoxOptions();
// 基础配置
options.addPreference("dom.webnotifications.enabled", false); // 禁用通知
options.addPreference("intl.accept_languages", "zh-CN"); // 中文语言
if (ConfigReader.isHeadless()) {
options.addArguments("--headless");
}
return new FirefoxDriver(options);
}
3.3.3 Edge 驱动创建
private static WebDriver createEdgeDriver() {
EdgeOptions options = new EdgeOptions();
// Edge 基于 Chromium,配置和 Chrome 很像
options.addArguments("--disable-notifications");
options.addArguments("--disable-popup-blocking");
if (ConfigReader.isHeadless()) {
options.addArguments("--headless");
options.addArguments("--disable-gpu");
}
return new EdgeDriver(options);
}
关键点:每个浏览器的 Options 类不同,但配置思路完全一样。你只要封装好一个 createDriver 方法,根据参数分发即可。
3.4 浏览器选项配置:无头模式与禁用 GPU
为什么要把无头模式和禁用 GPU 放在一起讲?因为这两个配置经常成对出现。
无头模式:浏览器在后台运行,不显示界面。适合 CI/CD 环境,比如 Jenkins 上跑测试,没人盯着屏幕看。
禁用 GPU:无头模式下,GPU 加速反而会拖慢速度,甚至导致某些页面渲染异常。所以一般会一起禁用。
来看配置文件的实现:
// ConfigReader.java
public class ConfigReader {
private static Properties props = new Properties();
static {
try {
// 从 resources 目录加载 config.properties
InputStream input = ConfigReader.class.getClassLoader()
.getResourceAsStream("config.properties");
props.load(input);
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取浏览器类型,默认 Chrome
public static String getBrowser() {
return props.getProperty("browser", "chrome");
}
// 是否启用无头模式,默认 false
public static boolean isHeadless() {
return Boolean.parseBoolean(props.getProperty("headless", "false"));
}
}
对应的配置文件 config.properties:
# 浏览器配置
browser=chrome
# 是否无头模式
headless=false
注意:无头模式不是万能的。有些前端动画、弹窗、文件下载等操作,在无头模式下可能表现异常。我建议:本地调试用有头模式,CI 环境用无头模式。别一上来就全无头,出了问题排查起来很痛苦。
3.5 完整使用示例
最后,咱们看看怎么用这个封装好的驱动管理器:
public class TestExample {
public static void main(String[] args) {
// 获取驱动实例
WebDriver driver = WebDriverSingleton.getDriver();
// 正常使用
driver.get("https://www.baidu.com");
System.out.println("页面标题: " + driver.getTitle());
// 测试结束后,统一关闭
WebDriverSingleton.quitDriver();
}
}
看到了吗?测试代码里完全不用关心浏览器是什么、要不要无头模式。这些都在配置文件和驱动封装里处理好了。
我的经验:封装驱动管理器时,一定要考虑线程安全。如果你的测试用例是并行执行的(比如用 TestNG 的 parallel),那单例模式需要加锁。不过那是进阶话题了,咱们先把基础版本跑通。
3.6 本章小结
咱们今天干了三件事:
- 单例模式:保证一个测试进程只有一个 WebDriver 实例,避免资源浪费
- 多浏览器支持:通过配置文件一键切换 Chrome/Firefox/Edge
- 选项配置:无头模式、禁用 GPU 等,灵活适应不同环境
下一章,咱们会深入 Page Object 模式,把页面元素和操作封装起来。到时候你会发现,有了今天这个驱动管理器,后面的工作会顺畅很多。
嗯,今天就到这儿。代码可以直接复制到你的项目里用,有问题随时翻翻这一章。