selenium webdriver 第二次初始化的异常

调试了几次,解决了第二次执行用例无法打开浏览器或者打开浏览器但是异常的情况:

deepseek的解释应该是配置项问题,改来改去,这一版没啥问题了

<selenium.version>4.27.0</selenium.version>

package com.automation.core.impl;

import com.automation.core.assertion.AssertionHelper;

import com.automation.core.config.ConfigLoader;

import com.automation.core.executor.ExecutionContext;

import com.automation.core.executor.TestExecutor;

import com.automation.core.model.TestCaseInfo;

import com.automation.core.model.TestCaseResult;

import com.automation.core.monitor.TestExecutionMonitor;

import com.automation.core.report.ExtentReportManager;

import com.automation.test.context.ArtifactStorage;

import com.automation.test.uibasetest.BaseTest;

import com.aventstack.extentreports.ExtentReports;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.chrome.ChromeDriverService;

import org.openqa.selenium.chrome.ChromeOptions;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.ApplicationContext;

import org.springframework.stereotype.Component;

import java.io.BufferedReader;

import java.io.File;

import java.io.InputStreamReader;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

import java.time.Duration;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

/**

* Selenium UI 测试执行器

* 所有配置从 application.yml 读取

*/

@Component

public class SeleniumTestExecutor implements TestExecutor {

private static final Logger log = LoggerFactory.getLogger(SeleniumTestExecutor.class);

private static final List<String> SUPPORTED_FRAMEWORKS = Arrays.asList("SELENIUM", "WEBDRIVER");

private static final ThreadLocal<WebDriver> driverThreadLocal = new ThreadLocal<>();

// 记录自己创建的进程(用于超时清理)

private static final Map<String, Long> managedDriverPids = new ConcurrentHashMap<>();

private static final long MAX_DRIVER_AGE = 300000; // 5分钟

@Autowired

private ConfigLoader configLoader;

@Autowired

private ApplicationContext applicationContext;

// 使用 ConcurrentHashMap 管理不同 executionId 对应的 WebDriver

private static final Map<String, WebDriver> driverMap = new ConcurrentHashMap<>();

private static final Map<String, String> driverStatus = new ConcurrentHashMap<>();

// 从 YAML 读取的配置项

private static String driverPath;

private static String browserPath;

private static boolean headless;

private static int timeout;

private static int implicitWait;

@Autowired

private ExtentReportManager extentReportManager;

@Autowired

private TestExecutionMonitor monitor;

@PostConstruct

public void init() {

// ✅ 从配置加载到静态变量

SeleniumTestExecutor.driverPath = configLoader.getString("webdriver.chrome.driver-path");

SeleniumTestExecutor.browserPath = configLoader.getString("webdriver.chrome.browser-path", null);

SeleniumTestExecutor.headless = configLoader.getBoolean("webdriver.browser.headless", false);

SeleniumTestExecutor.timeout = configLoader.getInt("webdriver.browser.timeout", 60);

SeleniumTestExecutor.implicitWait = configLoader.getInt("webdriver.browser.implicit-wait", 10);

// ✅ 设置系统属性(确保 Selenium Manager 被禁用)

System.setProperty("seleniummanager.enabled", "false");

System.setProperty("selenium.manager.enabled", "false");

System.setProperty("webdriver.chrome.driver", driverPath);

log.info(" SeleniumTestExecutor configuration loaded from YAML:");

log.info(" driver-path: {}", driverPath.isEmpty() ? "(not set, will auto-find)" : driverPath);

log.info(" browser-path: {}", browserPath.isEmpty() ? "(default)" : browserPath);

log.info(" headless: {}", headless);

log.info(" timeout: {}s", timeout);

log.info(" implicit-wait: {}s", implicitWait);

}

// ==================== 进程清理方法 ====================

/**

* 清理僵尸 ChromeDriver 进程

* 只清理没有关联 Chrome 浏览器窗口的 Chromedriver 进程

* 不会影响正在运行的测试

*/

private static void killZombieChromeDriverProcesses() {

String os = System.getProperty("os.name").toLowerCase();

if (!os.contains("win")) {

return;

}

try {

// 1. 获取所有 chromedriver.exe 进程

Process tasklist = Runtime.getRuntime().exec(

"tasklist /FI \"IMAGENAME eq chromedriver.exe\" /FO CSV /NH"

);

BufferedReader reader = new BufferedReader(new InputStreamReader(tasklist.getInputStream()));

List<String> driverPids = new ArrayList<>();

String line;

while ((line = reader.readLine()) != null) {

// CSV 格式: "chromedriver.exe","12345","Console","1","12,345 K"

String\[\] parts = line.split(",");

if (parts.length >= 2) {

String pid = parts1.replace("\"", "").trim();

if (pid.matches("\\d+")) {

driverPids.add(pid);

}

}

}

reader.close();

if (driverPids.isEmpty()) {

log.debug("没有发现 ChromeDriver 进程");

return;

}

log.debug("发现 {} 个 ChromeDriver 进程", driverPids.size());

// 2. 检查每个 ChromeDriver 是否是僵尸进程

int killedCount = 0;

for (String driverPid : driverPids) {

if (isZombieDriver(driverPid)) {

Process kill = Runtime.getRuntime().exec("taskkill /F /PID " + driverPid);

kill.waitFor();

killedCount++;

managedDriverPids.remove(driverPid);

log.info(" 已清理僵尸 ChromeDriver 进程, PID: {}", driverPid);

}

}

if (killedCount > 0) {

log.info(" 共清理 {} 个僵尸 ChromeDriver 进程", killedCount);

}

} catch (Exception e) {

log.debug("清理僵尸进程失败: {}", e.getMessage());

}

}

/**

* 判断 ChromeDriver 是否是僵尸进程

*/

private static boolean isZombieDriver(String driverPid) throws Exception {

// 1. 获取 ChromeDriver 的父进程 PID

Process wmic = Runtime.getRuntime().exec(

"wmic process where \"ProcessId=" + driverPid + "\" get ParentProcessId /VALUE"

);

BufferedReader reader = new BufferedReader(new InputStreamReader(wmic.getInputStream()));

String parentPid = null;

String line;

while ((line = reader.readLine()) != null) {

line = line.trim();

if (line.startsWith("ParentProcessId=")) {

parentPid = line.substring("ParentProcessId=".length()).trim();

break;

}

}

reader.close();

if (parentPid == null || parentPid.isEmpty() || "0".equals(parentPid)) {

return true; // 没有父进程,是僵尸

}

// 2. 检查父进程是否还在运行

Process check = Runtime.getRuntime().exec(

"tasklist /FI \"PID eq " + parentPid + "\" /FO CSV /NH"

);

BufferedReader checkReader = new BufferedReader(new InputStreamReader(check.getInputStream()));

String checkLine = checkReader.readLine();

checkReader.close();

if (checkLine == null || checkLine.isEmpty()) {

log.debug("发现孤立 ChromeDriver, 父进程已不存在, PID: {}", driverPid);

return true;

}

// 3. 检查父进程是否是 Java 或 Chrome

if (checkLine.toLowerCase().contains("java.exe") ||

checkLine.toLowerCase().contains("javaw.exe") ||

checkLine.toLowerCase().contains("chrome.exe")) {

log.debug("ChromeDriver PID: {} 正常 (父进程: {})", driverPid, parentPid);

return false;

}

log.debug("发现孤立 ChromeDriver, 父进程不是 Java/Chrome, PID: {}, 父进程: {}", driverPid, parentPid);

return true;

}

/**

* 清理超时的 ChromeDriver 进程

* 只清理由当前服务创建且超过 5 分钟的进程

*/

private static void cleanTimeoutDrivers() {

String os = System.getProperty("os.name").toLowerCase();

log.info("os: {} " , os);

if (!os.contains("win")) {

return;

}

try {

long now = System.currentTimeMillis();

List<String> pidsToRemove = new ArrayList<>();

for (Map.Entry<String, Long> entry : managedDriverPids.entrySet()) {

String pid = entry.getKey();

long startTime = entry.getValue();

long age = now - startTime;

// 如果超过 5 分钟,检查并清理

if (age > MAX_DRIVER_AGE) {

try {

if (isProcessExists(pid)) {

if (isZombieDriver(pid)) {

Process kill = Runtime.getRuntime().exec("taskkill /F /PID " + pid);

kill.waitFor();

pidsToRemove.add(pid);

log.info(" 清理超时 ChromeDriver, PID: {}, 存活: {}ms", pid, age);

} else {

log.debug("ChromeDriver 超时但仍有活动, PID: {}, 存活: {}ms", pid, age);

}

} else {

pidsToRemove.add(pid);

log.debug("ChromeDriver 进程已消失, PID: {}", pid);

}

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

for (String pid : pidsToRemove) {

managedDriverPids.remove(pid);

}

if (!pidsToRemove.isEmpty()) {

log.info("⏱ 共清理 {} 个超时 ChromeDriver 进程", pidsToRemove.size());

}

} catch (Exception e) {

log.debug("清理超时进程失败: {}", e.getMessage());

}

}

/**

* 检查进程是否存在

*/

private static boolean isProcessExists(String pid) throws Exception {

Process check = Runtime.getRuntime().exec(

"tasklist /FI \"PID eq " + pid + "\" /FO CSV /NH"

);

BufferedReader reader = new BufferedReader(new InputStreamReader(check.getInputStream()));

String line = reader.readLine();

reader.close();

return line != null && !line.isEmpty() && !line.contains("没有运行");

}

/**

* 获取 ChromeDriver 进程 PID

*/

private String getChromeDriverPid() {

String os = System.getProperty("os.name").toLowerCase();

if (!os.contains("win")) {

return null;

}

try {

String javaPid = getCurrentJavaPid();

if (javaPid == null) {

return null;

}

Process wmic = Runtime.getRuntime().exec(

"wmic process where \"name='chromedriver.exe'\" get ProcessId,ParentProcessId /FORMAT:CSV"

);

BufferedReader reader = new BufferedReader(new InputStreamReader(wmic.getInputStream()));

String line;

boolean first = true;

while ((line = reader.readLine()) != null) {

if (first) { first = false; continue; }

String\[\] parts = line.split(",");

if (parts.length >= 3) {

String pid = parts1.trim();

String parentPid = parts2.trim();

if (parentPid.equals(javaPid)) {

return pid;

}

}

}

reader.close();

} catch (Exception e) {

log.debug("获取 ChromeDriver PID 失败: {}", e.getMessage());

}

return null;

}

/**

* 获取当前 Java 进程 PID

*/

private String getCurrentJavaPid() {

try {

String name = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();

if (name != null && name.contains("@")) {

return name.split("@")0;

}

} catch (Exception e) {

log.debug("获取 Java PID 失败: {}", e.getMessage());

}

return null;

}

/**

* 注册自己创建的 ChromeDriver 进程

*/

private void registerDriverProcess(String pid) {

if (pid != null && pid.matches("\\d+")) {

managedDriverPids.put(pid, System.currentTimeMillis());

log.debug("注册 ChromeDriver 进程, PID: {}", pid);

}

}

// ==================== 获取配置辅助方法 ====================

private String getStringValue(Map<String, Object> config, String key, String defaultValue) {

if (config == null) {

return defaultValue;

}

Object value = config.get(key);

if (value != null && !value.toString().isEmpty()) {

return value.toString();

}

return defaultValue;

}

private boolean getBooleanValue(Map<String, Object> config, String key, boolean defaultValue) {

if (config == null) {

return defaultValue;

}

Object value = config.get(key);

if (value != null) {

return Boolean.parseBoolean(value.toString());

}

return defaultValue;

}

private int getIntValue(Map<String, Object> config, String key, int defaultValue) {

if (config == null) {

return defaultValue;

}

Object value = config.get(key);

if (value != null) {

try {

return Integer.parseInt(value.toString());

} catch (NumberFormatException e) {

log.warn(" Failed to parse int value for {}: {}, using default: {}", key, value, defaultValue);

}

}

return defaultValue;

}

// ==================== TestExecutor 接口实现 ====================

@Override

public TestCaseResult execute(TestCaseInfo testCase, ExecutionContext context) {

log.info("执行 Selenium 测试: {}", testCase.getName());

TestCaseResult result = new TestCaseResult();

result.setTestCaseId(testCase.getId());

result.setName(testCase.getName());

result.setStatus("RUNNING");

long startTime = System.currentTimeMillis();

String executionId = context.getExecutionId();

log.info("execute method executionId 获取{} " ,executionId);

try {

WebDriver driver = getDriver(executionId);

log.info("driver 获取{} " ,driver);

executeByReflection(testCase, driver, context);

result.setStatus("PASSED");

result.setDurationMs(System.currentTimeMillis() - startTime);

} catch (Exception e) {

log.error("测试执行失败: {}", e.getMessage(), e);

result.setStatus("FAILED");

result.setErrorMessage(e.getMessage());

result.setStackTrace(getStackTraceString(e));

result.setDurationMs(System.currentTimeMillis() - startTime);

} finally {

// closeDriver(executionId);

// log.info("浏览器已关闭,测试执行结束");

}

return result;

}

@Override

public String getType() {

return "UI";

}

@Override

public boolean supports(TestCaseInfo testCase) {

if (testCase == null || testCase.getFramework() == null) {

return false;

}

return SUPPORTED_FRAMEWORKS.contains(testCase.getFramework().toUpperCase());

}

/**

* 同一个 executionId:多个用例串行执行,共享同一个浏览器会话(复用 WebDriver)

* 不同的 executionId:新的执行批次,重新初始化浏览器

* 这样既能提高执行效率(避免重复启动浏览器),又能正确隔离不同的执行批次

* @param executionId

* @return

*/

// ==================== WebDriver 初始化 ====================

/**

private WebDriver getDriver(String executionId) {

WebDriver driver = driverThreadLocal.get();

if (driver == null) {

log.info(" 初始化 WebDriver for thread: {}", Thread.currentThread().getName());

// ✅ 创建前清理进程

cleanTimeoutDrivers(); // 方案三:清理超时进程

killZombieChromeDriverProcesses(); // 方案二:清理僵尸进程

setupDriverPath();

ChromeOptions options = createChromeOptions();

try {

driver = new ChromeDriver(options);

} catch (Exception e) {

log.error(" 创建 ChromeDriver 失败: {}", e.getMessage(), e);

// 重试一次,先清理再创建

killZombieChromeDriverProcesses();

cleanTimeoutDrivers();

try {

driver = new ChromeDriver(options);

log.info(" 重试创建 ChromeDriver 成功");

} catch (Exception e2) {

throw new RuntimeException("ChromeDriver 初始化失败,请检查:\n" +

"1. ChromeDriver 路径是否正确: " + System.getProperty("webdriver.chrome.driver") + "\n" +

"2. Chrome 浏览器是否已安装\n" +

"3. ChromeDriver 版本是否与 Chrome 浏览器版本匹配", e2);

}

}

// 设置窗口大小

driver.manage().window().setSize(new Dimension(windowWidth, windowHigh));

// 设置超时

driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(timeout));

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(implicitWait));

driverThreadLocal.set(driver);

// ✅ 注册进程(用于超时管理)

try {

String pid = getChromeDriverPid();

if (pid != null) {

registerDriverProcess(pid);

}

} catch (Exception e) {

log.debug("注册进程失败: {}", e.getMessage());

}

log.info(" WebDriver 初始化成功");

}

return driver;

}

**/

/**

* 获取指定 executionId 的 WebDriver

* 如果不存在则创建新的,如果存在则复用

*/

public static WebDriver getDriver(String executionId) {

log.info("getDriver 入参 executionId {}" ,executionId);

// 检查是否已有该 executionId 的 WebDriver

WebDriver existingDriver = driverMap.get(executionId);

log.info("driverMap 第一次查询 driver 入参 executionId {}, driver {} " ,executionId, existingDriver);

if (existingDriver != null) {

try {

// 验证 driver 是否仍然有效

existingDriver.getTitle();

log.info(" 复用 WebDriver: executionId={}", executionId);

driverThreadLocal.set(existingDriver);

driverStatus.put(executionId, "ACTIVE");

return existingDriver;

} catch (Exception e) {

// driver 已失效,移除并重新创建

log.warn("WebDriver 已失效,重新创建: executionId={}", executionId);

driverMap.remove(executionId);

driverStatus.remove(executionId);

}

}

// 创建新的 WebDriver

log.info(" 创建新的 WebDriver: executionId={}", executionId);

WebDriver driver = null;

try {

driver = createNewDriver(executionId);

driverMap.put(executionId, driver);

driverStatus.put(executionId, "ACTIVE");

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return driver;

}

/**

* 创建新的 WebDriver

*/

private static WebDriver createNewDriver(String executionId) {

/*

* String driverPath1 = System.getProperty("webdriver.chrome.driver"); log.

* info("create new driver SeleniumTestExecutor.System.getProperty(\"webdriver.chrome.driver\") : {} "

* ,driverPath1 ); File driverFile = new File(driverPath1);

*

* if (!driverFile.exists()) { log.info("SeleniumTestExecutor.driverPath : {} "

* ,SeleniumTestExecutor.driverPath );

* System.setProperty("webdriver.chrome.driver",

* SeleniumTestExecutor.driverPath); }

*/

ChromeOptions options = createChromeOptions(executionId);

setupDriverPath();

// 设置浏览器路径(如果默认路径不对)

// options.setBinary("C:/Program Files/Google/Chrome/Application/chrome.exe");

// 增加超时时间

System.setProperty("webdriver.chrome.driver.timeout", "120");

// 设置日志级别,便于调试

System.setProperty("webdriver.chrome.logfile", "chromedriver.log");

System.setProperty("webdriver.chrome.verboseLogging", "true");

String binaryPath = browserPath;

if (binaryPath == null || binaryPath.isEmpty()) {

log.warn(" browser-path 未配置,尝试自动查找 Chrome");

String foundPath = null;

try {

foundPath = findChromeBinaryAuto();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

if (foundPath != null) {

binaryPath = foundPath;

log.info(" 自动找到 Chrome: {}", binaryPath);

}

}

if (binaryPath != null && !binaryPath.isEmpty()) {

options.setBinary(binaryPath);

log.info(" 使用浏览器: {}", binaryPath);

} else {

log.warn(" 未找到 Chrome 浏览器路径,将使用 ChromeDriver 默认查找机制");

}

// 打印当前配置信息

log.info("ChromeOptions 配置:");

log.info(" driver-path: {}", System.getProperty("webdriver.chrome.driver"));

log.info(" browser-path: {}", binaryPath != null ? binaryPath : "default");

log.info(" headless: {}", headless);

// 重试机制

int maxRetries = 3;

Exception lastException = null;

for (int attempt = 1; attempt <= maxRetries; attempt++) {

try {

log.info("尝试创建 ChromeDriver, 第 {}/{} 次", attempt, maxRetries);

// 清理旧的用户目录

String userDataDir = "C:/temp/chrome-profile-" + executionId;

deleteDirectory(new File(userDataDir));

WebDriver driver = new ChromeDriver(options);

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);

driver.manage().timeouts().setScriptTimeout(60, TimeUnit.SECONDS);

driverThreadLocal.set(driver);

log.info(" ChromeDriver 创建成功: executionId={}", executionId);

return driver;

} catch (Exception e) {

killZombieChromeDriverProcesses();

cleanTimeoutDrivers();

lastException = e;

log.warn("第 {} 次创建失败: {}", attempt, e.getMessage());

// 等待后重试

if (attempt < maxRetries) {

try {

Thread.sleep(2000);

} catch (InterruptedException ignored) {}

}

}finally{

killZombieChromeDriverProcesses();

cleanTimeoutDrivers();

}

}

throw new RuntimeException("ChromeDriver 初始化失败,请检查:\n" +

"1. ChromeDriver 路径是否正确: " + System.getProperty("webdriver.chrome.driver") + "\n" +

"2. Chrome 浏览器是否已安装\n" +

"3. ChromeDriver 版本是否与 Chrome 浏览器版本匹配", lastException);

}

private static void deleteDirectory(File dir) {

if (dir.exists()) {

File\[\] files = dir.listFiles();

if (files != null) {

for (File file : files) {

deleteDirectory(file);

}

}

dir.delete();

}

}

/**

* 关闭指定 executionId 的 WebDriver

*/

public static void closeDriver(String executionId) {

if (executionId == null || executionId.isEmpty()) {

log.warn("executionId 为空,跳过关闭");

return;

}

WebDriver driver = driverMap.get(executionId);

driverStatus.remove(executionId);

if (driver != null) {

driverMap.remove(executionId);

driverThreadLocal.remove();

try {

driver.quit();

// 创建前清理进程

cleanTimeoutDrivers(); // 方案三:清理超时进程

killZombieChromeDriverProcesses(); // 方案二:清理僵尸进程

log.info(" WebDriver 已关闭: executionId={}", executionId);

} catch (Exception e) {

log.warn("关闭 WebDriver 异常: executionId={}, error={}", executionId, e.getMessage());

}

} else {

log.debug("未找到要关闭的 WebDriver: executionId={}", executionId);

}

}

/**

* 关闭所有 WebDriver(应用关闭时调用)

*/

public static void closeAllDrivers() {

log.info("关闭所有 WebDriver,共 {} 个", driverMap.size());

for (String executionId : driverMap.keySet()) {

closeDriver(executionId);

}

driverMap.clear();

driverStatus.clear();

driverThreadLocal.remove();

}

/**

* 检查指定 executionId 是否有活跃的 WebDriver

*/

public static boolean hasActiveDriver(String executionId) {

if (executionId == null || executionId.isEmpty()) {

return false;

}

WebDriver driver = driverMap.get(executionId);

if (driver == null) {

return false;

}

try {

driver.getTitle();

return true;

} catch (Exception e) {

driverMap.remove(executionId);

driverStatus.remove(executionId);

return false;

}

}

/**

* 获取当前所有活跃的 executionId

*/

public static Map<String, String> getActiveDrivers() {

return new ConcurrentHashMap<>(driverStatus);

}

// ==================== ChromeDriver 路径设置 ====================

private static void setupDriverPath() {

String driverPath = SeleniumTestExecutor.driverPath;

log.info("default driverpath: {}" ,driverPath);

if (driverPath != null && !driverPath.isEmpty()) {

setDriverPath(driverPath);

return;

}

String foundPath = null;

try {

foundPath = findDriverAuto();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

if (foundPath != null) {

setDriverPath(foundPath);

return;

}

throw new RuntimeException("无法找到 ChromeDriver,请在 application.yml 中配置 webdriver.chrome.driver-path");

}

private static String findDriverAuto() {

String os = System.getProperty("os.name").toLowerCase();

String driverName = os.contains("win") ? "chromedriver.exe" : "chromedriver";

String\[\] possiblePaths = {

System.getProperty("user.dir") + "/drivers/" + driverName,

System.getProperty("user.dir") + "/src/test/resources/drivers/" + driverName,

System.getProperty("user.dir") + "/target/classes/drivers/" + driverName,

System.getProperty("user.home") + "/.drivers/" + driverName,

System.getProperty("user.home") + "/Downloads/chromedriver-win64/" + driverName,

System.getProperty("user.home") + "/Downloads/" + driverName

};

for (String path : possiblePaths) {

File file = new File(path);

if (file.exists() && file.isFile()) {

if (os.contains("win") || file.canExecute()) {

log.info(" 找到默认 ChromeDriver: {}", path);

return path;

}

}

}

log.warn(" 未找到默认 ChromeDriver,请配置 webdriver.chrome.driver-path");

return null;

}

private static void setDriverPath(String driverPath) {

File driverFile = new File(driverPath);

if (!driverFile.isAbsolute()) {

String basePath = System.getProperty("user.dir");

driverFile = new File(basePath, driverPath);

}

String absolutePath = driverFile.getAbsolutePath();

System.setProperty("webdriver.chrome.driver", absolutePath);

log.info(" 设置 ChromeDriver 路径: {}", absolutePath);

if (!driverFile.exists()) {

log.error(" ChromeDriver 文件不存在: {}", absolutePath);

throw new RuntimeException("ChromeDriver 文件不存在: " + absolutePath);

}

String os = System.getProperty("os.name").toLowerCase();

if (!os.contains("win") && !driverFile.canExecute()) {

log.warn(" ChromeDriver 文件不可执行,尝试设置执行权限");

try {

driverFile.setExecutable(true);

} catch (Exception e) {

log.warn("设置执行权限失败: {}", e.getMessage());

}

}

}

private static String findChromeBinaryAuto() {

String os = System.getProperty("os.name").toLowerCase();

if (os.contains("win")) {

String\[\] possiblePaths = {

"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",

"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",

System.getProperty("user.home") + "\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"

};

for (String path : possiblePaths) {

File file = new File(path);

if (file.exists() && file.isFile()) {

return path;

}

}

}

return null;

}

private static ChromeOptions createChromeOptions(String executionId) {

ChromeOptions options = new ChromeOptions();

// 核心参数

options.addArguments("--remote-allow-origins=*");

options.addArguments("--start-maximized");

options.addArguments("--disable-dev-shm-usage");

options.addArguments("--no-sandbox");

options.addArguments("--disable-gpu");

options.addArguments("--remote-debugging-port=0");

options.addArguments("--disable-blink-features=AutomationControlled");

options.addArguments("--no-first-run");

options.addArguments("--disable-default-apps");

options.addArguments("--disable-sync");

options.addArguments("--disable-features=ChromeWhatsNewUI,PrivacySandboxPrompt,SigninPromo");

// 为每次执行创建一个新的临时用户目录

String userDataDir = "C:/temp/chrome-profile-" + executionId;

log.info("为每次执行创建一个新的临时用户目录 : {} " ,userDataDir );

options.addArguments("--user-data-dir=" + userDataDir);

if (headless) {

options.addArguments("--headless");

log.info(" 启用无头模式 (from YAML)");

}

log.info("ChromeOptions 配置:");

return options;

}

// ==================== 反射执行 ====================

private void executeByReflection(TestCaseInfo testCase, WebDriver driver, ExecutionContext context) throws Exception {

ExtentReports extentReports = extentReportManager.initReport(context.getExecutionId());

Class<?> clazz = Class.forName(testCase.getClassName());

Object instance;

try {

instance = applicationContext.getBean(clazz);

log.info("获取 Spring Bean: {}", testCase.getClassName());

} catch (Exception e) {

instance = clazz.getDeclaredConstructor().newInstance();

log.info("反射创建实例: {}", testCase.getClassName());

}

if (instance instanceof BaseTest) {

BaseTest baseTest = (BaseTest) instance;

baseTest.setDriver(driver);

// 2. 验证 driver 是否真的被设置了

if (baseTest.getDriver() == null) {

throw new RuntimeException("Driver 注入失败,请检查 BaseTest.setDriver() 方法");

}

log.info("Driver 已注入到: {}", testCase.getClassName());

baseTest.setContext(context);

log.info("Context 已注入到: {}", testCase.getClassName());

// 注入 monitor

setField(baseTest, "monitor", monitor);

log.info("Monitor 已注入到: {}", testCase.getClassName());

// 创建并注入 AssertionHelper

AssertionHelper assertionHelper = new AssertionHelper();

assertionHelper.setMonitor(monitor);

baseTest.setAssertion(assertionHelper);

log.info("AssertionHelper 已注入到: {}", testCase.getClassName());

// 注入 configLoader

baseTest.setConfigLoader(configLoader);

log.info("ConfigLoader 已注入到: {}", testCase.getClassName());

// 设置产物存储

String artifactBasePath = "test-output/executions/" + context.getExecutionId();

ArtifactStorage storage = new ArtifactStorage(artifactBasePath);

baseTest.setArtifactStorage(storage);

if (monitor != null) {

monitor.setArtifactStorage(storage);

}

log.info("ArtifactStorage 已注入到: {}", testCase.getClassName());

// 设置 ExtentReports

baseTest.setExtentReports(extentReports);

log.info("ExtentReports 已注入到: {}", testCase.getClassName());

}

// 执行测试

Method method = clazz.getMethod(testCase.getMethodName());

method.invoke(instance);

// 获取测试结果

TestCaseResult result = monitor.getResult();

if (result != null) {

context.setSharedData("testResult", result);

log.info("测试结果: {}", result.getStatus());

}

// 生成报告

extentReportManager.generateReport();

}

/**

* 通过反射设置字段值

*/

private void setField(Object target, String fieldName, Object value) {

try {

Field field = target.getClass().getDeclaredField(fieldName);

field.setAccessible(true);

field.set(target, value);

} catch (Exception e) {

log.warn("设置字段失败: {}.{} - {}", target.getClass().getSimpleName(), fieldName, e.getMessage());

}

}

private String getStackTraceString(Throwable e) {

StringBuilder sb = new StringBuilder();

sb.append(e.toString()).append("\n");

for (StackTraceElement element : e.getStackTrace()) {

sb.append(" at ").append(element.toString()).append("\n");

}

return sb.toString();

}

public static void closeDriver() {

WebDriver driver = driverThreadLocal.get();

if (driver != null) {

try {

driver.quit();

log.info("WebDriver 已关闭");

} catch (Exception e) {

log.warn("关闭 WebDriver 异常: {}", e.getMessage());

} finally {

driverThreadLocal.remove();

}

}

}

}

相关推荐
Csvn1 小时前
🐍 Day 4: Python 控制流 — 条件、循环与推导式的艺术
后端·python
动词ing2 小时前
【C语言】自定义函数+指针入门
c语言·开发语言·算法
重生之后端学习2 小时前
283. 移动零[简单]✅
开发语言·数据结构·算法·leetcode·职场和发展
动词ing3 小时前
【C语言】结构体+文件基础
c语言·开发语言·数据结构
caimouse3 小时前
ReactOS 窗口系统分析(7):分层窗口与绘制辅助 — layered.c + draw.c
c语言·开发语言
丰锋ff3 小时前
基于 Qt 的智慧社区物业管理系统
开发语言·qt
夜不会漫长4 小时前
C++入门(1)
开发语言·c++·算法
大鹏说大话4 小时前
从爬虫到决策引擎:大数据下自媒体如何用Python挖掘用户痛点
开发语言·爬虫·python
Niuguangshuo4 小时前
silero-vad:超轻量级开源 VAD 实践指南
开发语言·python