如何在自定义Browser类中实现Selenium DevTools的getDevTools()方法
在自定义Browser类中实现Selenium DevTools获取功能
问题背景
使用Selenium 4.8时,直接实例化ChromeDriver可以轻松获取DevTools:
ChromeDriver driver = new ChromeDriver(); DevTools devTool = driver.getDevTools(); devTool.createSession();
但测试依赖自定义Browser类(代码如下),该类根据配置返回本地/远程WebDriver,现在需要在这个类中实现DevTools的获取能力,目标是支持以下调用方式:
WebDriver extendedDriver = Browser.extendedDriver(); DevTools devTool = extendedDriver.getDevTools(); devTool.createSession();
自定义Browser类代码
import lombok.extern.slf4j.Slf4j; import <LOCAL ADDRESS REMOVED>.selenium.config.BrowserConfig; import <LOCAL ADDRESS REMOVED>.selenium.config.SeleniumOptions; import <LOCAL ADDRESS REMOVED>.selenium.driver.ExtendedWebDriver; import <LOCAL ADDRESS REMOVED>.selenium.driver.WebDriverFactory; import org.openqa.selenium.InvalidArgumentException; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.LocalFileDetector; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.support.ui.WebDriverWait; import java.net.MalformedURLException; import java.net.URL; import java.security.InvalidParameterException; import java.time.Duration; import java.util.List; import java.util.concurrent.TimeUnit; import static java.lang.String.format; @Slf4j public class Browser { public static final int DEFAULT_IMPLICIT_WAIT = 0; public static final int DEFAULT_SCRIPT_TIMEOUT = 20; public static final int DEFAULT_WAIT_TIMEOUT = 30; public static long globalWaitTimeout; private static boolean useRemoteDriver = false; private static SeleniumOptions.WebAppTeknologi webAppTeknologi = SeleniumOptions.WebAppTeknologi.INGEN; private static ThreadLocal<WebDriver> driver=new ThreadLocal<>(); private static ThreadLocal<ExtendedWebDriver> extendedDriver=new ThreadLocal<>(); private static ThreadLocal<JavascriptExecutor> jsExecutor=new ThreadLocal<>(); private static ThreadLocal<WebDriverWait> standardWait=new ThreadLocal<>(); private Browser() {} public static void init(BrowserConfig browserConfig, List<String> chromeOptionsParameters) throws MalformedURLException { Browser.webAppTeknologi = browserConfig.getWebAppTeknologi(); String seleniumGridUrl = browserConfig.getSeleniumGridAdresse(); if (StringUtils.isNotBlank(seleniumGridUrl)) { for (int i = 0; i <= 5; i++) { try { opprettEksternBrowser(browserConfig.getNettleserType(), new URL(seleniumGridUrl)); break; } catch (org.openqa.selenium.remote.UnreachableBrowserException unreachableBrowserException) { log.debug(String.format("Klarte ikke starte browser mot %s på forsøk nr %s. Venter 10 s",seleniumGridUrl,i)); try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } else { opprettLokalBrowser(browserConfig.getNettleserType(), chromeOptionsParameters); } setBrowserTimeouts(DEFAULT_SCRIPT_TIMEOUT, DEFAULT_IMPLICIT_WAIT, DEFAULT_WAIT_TIMEOUT); } private static void opprettLokalBrowser(SeleniumOptions.NettleserType browser, List<String> chromeOptionsParameters) { switch (browser) { case CHROME: final ChromeOptions chromeOptions = new ChromeOptions(); if (chromeOptionsParameters != null) { for (String parameter : chromeOptionsParameters) { chromeOptions.addArguments(parameter); } } driver.set(new ChromeDriver(chromeOptions)); log.debug("Chrome valgt som ønsket nettleser."); break; case CHROME_HEADLESS: final ChromeOptions chromeOptions2 = new ChromeOptions(); chromeOptions2.setHeadless(true); driver.set(new ChromeDriver(chromeOptions2)); log.debug("Chrome Headless valgt som ønsket nettleser."); break; default: throw new InvalidArgumentException(format("Nettleser '%s' ikke implementert i rammeverk", browser.name())); } } private static void opprettEksternBrowser(SeleniumOptions.NettleserType browser, URL seleniumGridUrl) { useRemoteDriver = true; RemoteWebDriver remoteWebDriver; switch (browser) { case CHROME: final ChromeOptions chromeOptions = new ChromeOptions(); remoteWebDriver = new RemoteWebDriver(seleniumGridUrl, chromeOptions); log.debug("Remote (Chrome) valgt som ønsket nettleser."); break; default: throw new InvalidParameterException(format("Nettleser %s for selenium grid ikke implementert i rammeverk", browser.name())); } remoteWebDriver.setFileDetector(new LocalFileDetector()); // For filopplasting fra lokal-path driver.set(remoteWebDriver); } public static void setBrowserTimeouts(int scriptTimeout, int implicitWait, int waitTimeout) { if (driver == null) { throw new NullPointerException("Browser.init() må kjøres før man kan kalle driver-instansen"); } driver.get().manage().timeouts().setScriptTimeout(scriptTimeout, TimeUnit.SECONDS); driver.get().manage().timeouts().implicitlyWait(implicitWait, TimeUnit.SECONDS); globalWaitTimeout = (long) waitTimeout; standardWait.set(new WebDriverWait(driver.get(), Duration.ofSeconds(waitTimeout))); } public static WebDriver vanillaDriver() { if (driver.get() == null) { throw new NullPointerException("Browser.init() må kjøres før man kan kalle driver-instansen"); } return driver.get(); } public static ExtendedWebDriver extendedDriver() { if (extendedDriver.get() != null) { return extendedDriver.get(); } if (getWebAppType().equals(SeleniumOptions.WebAppTeknologi.INGEN)) { throw new RuntimeException("Ingen WebAppTeknologi satt, bruk vanillaDriver() eller sett WebAppTeknologi"); } extendedDriver.set(WebDriverFactory.getExtendedWebDriver(vanillaDriver(), webAppTeknologi)); return extendedDriver.get(); } public static JavascriptExecutor jsExecutor() { if (jsExecutor.get() == null) { WebDriver jsDriver = vanillaDriver(); jsExecutor.set((JavascriptExecutor) jsDriver); } return jsExecutor.get(); } public static WebDriverWait Wait() { if (standardWait.get() == null) { throw new NullPointerException("StandardWait er ikke satt"); } return standardWait.get(); } public static WebDriverWait Wait(long timeOutInSeconds) { return new WebDriverWait(vanillaDriver(), Duration.ofSeconds(timeOutInSeconds)); } public static boolean isRemoteDriver() { return useRemoteDriver; } public static SeleniumOptions.WebAppTeknologi getWebAppType() { return webAppTeknologi; } }
解决方案
步骤1:添加DevTools的线程安全存储
在Browser类的静态变量区新增ThreadLocal,保证多线程测试下的资源隔离:
import org.openqa.selenium.devtools.DevTools; // ... 保留原有导入 private static ThreadLocal<DevTools> devTools = new ThreadLocal<>();
步骤2:实现getDevTools方法
在Browser类中新增获取DevTools的方法,兼容本地ChromeDriver和远程Chrome驱动:
public static DevTools getDevTools() { if (devTools.get() != null) { return devTools.get(); } WebDriver currentDriver = vanillaDriver(); DevTools devTool; // 处理本地ChromeDriver实例 if (currentDriver instanceof ChromeDriver) { devTool = ((ChromeDriver) currentDriver).getDevTools(); } // 处理远程Chrome驱动(Selenium 4+支持RemoteWebDriver直接获取DevTools) else if (currentDriver instanceof RemoteWebDriver && currentDriver.getClass().getName().contains("Chrome")) { devTool = ((RemoteWebDriver) currentDriver).getDevTools(); } // 不支持的浏览器类型抛出异常 else { throw new UnsupportedOperationException("当前浏览器类型不支持DevTools功能"); } devTool.createSession(); devTools.set(devTool); return devTool; }
步骤3:让ExtendedWebDriver支持直接调用
如果要通过Browser.extendedDriver().getDevTools()直接调用,需要修改自定义的ExtendedWebDriver接口:
public interface ExtendedWebDriver extends WebDriver { // ... 保留原有方法 DevTools getDevTools(); }
然后在对应的实现类中实现该方法:
public class YourExtendedWebDriverImpl implements ExtendedWebDriver { // ... 保留原有实现 @Override public DevTools getDevTools() { return Browser.getDevTools(); } }
可选:添加资源清理逻辑
在Browser类的quit方法中(如果已有)新增DevTools的清理代码,避免资源泄漏:
public static void quit() { // 清理DevTools资源 if (devTools.get() != null) { devTools.get().close(); devTools.remove(); } // 清理其他驱动资源 if (driver.get() != null) { driver.get().quit(); driver.remove(); } if (extendedDriver.get() != null) { extendedDriver.remove(); } if (jsExecutor.get() != null) { jsExecutor.remove(); } if (standardWait.get() != null) { standardWait.remove(); } }
使用方式
现在可以直接通过以下代码获取并使用DevTools:
ExtendedWebDriver extendedDriver = Browser.extendedDriver(); DevTools devTool = extendedDriver.getDevTools(); // 示例:启用网络拦截等DevTools功能 devTool.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));
内容的提问来源于stack exchange,提问作者kakemonsteret
相关产品推荐
相关产品推荐

