并行 运行 的 Selenium 测试(使用 ThreadLocal)导致打开孤立的浏览器
Parallel run of Selenium tests (uses ThreadLocal) results in orphaned browsers being opened
我使用 ThreadLocal 实现线程安全,运行 使用 Maven 故障安全和 Junit 并行测试。我正在 运行ning 来自两个功能文件的两个测试以并行测试 运行ning。
但我总是让第一个浏览器空白。然后后续的就没问题了,测试也通过了。
如果我按顺序运行,没有问题。
HookStep class:-
public class HookStep {
@Before()
public void beginTest() {
WebDriverFactory.setDriver(Props.getValue("browser.name"));
}
@After()
public void stopTest(Scenario scenario) {
switch (environment) {
case "local":
case "aws": {
if (scenario.isFailed()) {
Screenshots.Shot shot = new Screenshots(Screenshots.CONTEXT_TEST_FAIL)
.takeShot(scenario.getName() + formCounter.getAndIncrement() + "");
scenario.embed(shot.getContent(), "image/png", "Error - ");
}
WebDriverFactory.closeBrowser();
}
}
WebDriverFactory class :-
public class WebDriverFactory {
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static synchronized void setDriver(String browser) {
switch (browser) {
case "chrome":
driver = ThreadLocal.withInitial(() -> {
WebDriverManager.chromedriver().setup();
return new ChromeDriver(BrowserOptions.getChromeOptions());
});
prepareBrowser();
break;
case "fireFox":
driver = ThreadLocal.withInitial(() -> {
WebDriverManager.firefoxdriver().setup();
return new FirefoxDriver(BrowserOptions.getFirefoxOptions());
});
break;
default:
throw new IllegalStateException("Unexpected value: " + browser);
}
}
private static void prepareBrowser(){
getDriver().manage().window().maximize();
getDriver().manage().deleteAllCookies();
getDriver().manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
getDriver().manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
}
public static synchronized WebDriver getDriver(){
return driver.get();
}
public static void closeBrowser() {
getDriver().quit();
}
}
StepDef Class:-
public class SampleStepDef {
private final WorldHelper helper;
public SampleStepDef(WorldHelper helper) {
this.helper = helper;
}
@Given("I click on the URL")
public void iClickOnTheURL() {
helper.getSamplePage().navigateToSite();
}
}
public class WorldHelper {
WebDriverFactory webDriverFactory=new WebDriverFactory();
protected WebDriver webDriver = webDriverFactory.getDriver();
private BasePage basePage;
private SamplePage samplePage;
public SamplePage getSamplePage(){
if(samplePage!=null) return samplePage;
samplePage= PageFactory.initElements(webDriver,SamplePage.class);
return samplePage;
}
}
public class SamplePage extends BasePage {
public SamplePage(WebDriver webDriver){
super(webDriver);
}
public void navigateToSite() {
webDriver.get("https://www.bbc.co.uk");
webDriver.findElement(By.xpath("//a[contains(text(),\'News\')]")).click();
}
}
public class BasePage extends WorldHelper {
public BasePage(WebDriver driver) {
this.webDriver = driver;
}
}
任何建议都会很有帮助。谢谢
我没有使用过 webDriverFactory,但我会尝试在工厂 class 中调用 driver.set(),如本教程所示:
http://makeseleniumeasy.com/2020/05/27/threadlocal-static-webdriver-for-parallel-execution/
我注意到与您的代码相关的多个问题。
- 您正在使用
ThreadLocal.withInitial()
。理想情况下,这应该在实例化 driver
线程局部静态变量时定义。
所以不用
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
应该是
private static final ThreadLocal<WebDriver> driver = ThreadLocal.withInitial(() -> {
return null; //Your supplier goes here.
});
您的继承层次结构明显混乱(很有可能您正在尝试创建一个简单的示例并且可能省略了继承层背后的细节),但是它不清楚为什么你所有的页面对象 classes 扩展 WorldHelper
您在 class 级别有多个语句,例如这样。这些字段级初始化的问题是它们在构造对象时被调用。因此,如果对象是在不同的线程中构建的,那么您 运行 就会遇到为该线程触发 WebDriver 初始化的问题。最终结果:您有很多不断打开的 ghost 浏览器实例,但没有针对它们的 selenium 操作。
private final WebDriver driver = WebDriverFactory.getDriver();
在使用 WebDriver 管理的 ThreadLocal
变体时,您需要确保您的调用始终来自您的步骤定义,而不是来自构造函数或来自 class 级别字段初始化,例如以上。
这是您需要执行的修复列表。
删除代码中出现的所有 private final WebDriver driver = WebDriverFactory.getDriver();
。不需要它们。
重构您的 WebDriverFactory class 如下所示(为简洁起见,我删除了所有注释掉的代码)
public class WebDriverFactory {
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static void setDriver(String browser) {
RemoteWebDriver rwd;
switch (browser) {
case "chrome":
WebDriverManager.chromedriver().setup();
rwd = new ChromeDriver(BrowserOptions.getChromeOptions());
break;
case "fireFox":
WebDriverManager.firefoxdriver().setup();
rwd = new FirefoxDriver(BrowserOptions.getFirefoxOptions());
break;
default:
throw new IllegalStateException("Unexpected value: " + browser);
}
driver.set(Objects.requireNonNull(rwd));
prepareBrowser();
}
private static void prepareBrowser(){
getDriver().manage().window().maximize();
getDriver().manage().deleteAllCookies();
getDriver().manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
getDriver().manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
}
public static WebDriver getDriver(){
return Objects.requireNonNull(driver.get());
}
public static void closeBrowser() {
getDriver().manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
getDriver().close();
getDriver().quit();
}
}
- 因为你的所有页面 classes 似乎都是从
WorldHelper
扩展而来的,所以添加一个 getter 方法,如下所示(或)确保你的任何地方都没有页面 classes 你有一个 WebDriver
字段。每当您需要获取 WebDriver 实例时,您应该直接通过 WebDriverFactory.getDriver()
(或)通过 getter 方法(例如下面在您的 WorldHelper
或任何基础 class 你正在创作。
protected WebDriver getDriver() {
return WebDriverFactory.getDriver();
}
一旦您解决了上述问题,您应该就没事了,应该不会看到任何空白浏览器 windows 打开。
注意:请在 GitHub 上清理您的项目。我注意到其中有一些云服务提供商凭证(可能是真实的凭证,也可能是假的。我不知道)
我使用 ThreadLocal 实现线程安全,运行 使用 Maven 故障安全和 Junit 并行测试。我正在 运行ning 来自两个功能文件的两个测试以并行测试 运行ning。 但我总是让第一个浏览器空白。然后后续的就没问题了,测试也通过了。 如果我按顺序运行,没有问题。
HookStep class:-
public class HookStep {
@Before()
public void beginTest() {
WebDriverFactory.setDriver(Props.getValue("browser.name"));
}
@After()
public void stopTest(Scenario scenario) {
switch (environment) {
case "local":
case "aws": {
if (scenario.isFailed()) {
Screenshots.Shot shot = new Screenshots(Screenshots.CONTEXT_TEST_FAIL)
.takeShot(scenario.getName() + formCounter.getAndIncrement() + "");
scenario.embed(shot.getContent(), "image/png", "Error - ");
}
WebDriverFactory.closeBrowser();
}
}
WebDriverFactory class :-
public class WebDriverFactory {
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static synchronized void setDriver(String browser) {
switch (browser) {
case "chrome":
driver = ThreadLocal.withInitial(() -> {
WebDriverManager.chromedriver().setup();
return new ChromeDriver(BrowserOptions.getChromeOptions());
});
prepareBrowser();
break;
case "fireFox":
driver = ThreadLocal.withInitial(() -> {
WebDriverManager.firefoxdriver().setup();
return new FirefoxDriver(BrowserOptions.getFirefoxOptions());
});
break;
default:
throw new IllegalStateException("Unexpected value: " + browser);
}
}
private static void prepareBrowser(){
getDriver().manage().window().maximize();
getDriver().manage().deleteAllCookies();
getDriver().manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
getDriver().manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
}
public static synchronized WebDriver getDriver(){
return driver.get();
}
public static void closeBrowser() {
getDriver().quit();
}
}
StepDef Class:-
public class SampleStepDef {
private final WorldHelper helper;
public SampleStepDef(WorldHelper helper) {
this.helper = helper;
}
@Given("I click on the URL")
public void iClickOnTheURL() {
helper.getSamplePage().navigateToSite();
}
}
public class WorldHelper {
WebDriverFactory webDriverFactory=new WebDriverFactory();
protected WebDriver webDriver = webDriverFactory.getDriver();
private BasePage basePage;
private SamplePage samplePage;
public SamplePage getSamplePage(){
if(samplePage!=null) return samplePage;
samplePage= PageFactory.initElements(webDriver,SamplePage.class);
return samplePage;
}
}
public class SamplePage extends BasePage {
public SamplePage(WebDriver webDriver){
super(webDriver);
}
public void navigateToSite() {
webDriver.get("https://www.bbc.co.uk");
webDriver.findElement(By.xpath("//a[contains(text(),\'News\')]")).click();
}
}
public class BasePage extends WorldHelper {
public BasePage(WebDriver driver) {
this.webDriver = driver;
}
}
任何建议都会很有帮助。谢谢
我没有使用过 webDriverFactory,但我会尝试在工厂 class 中调用 driver.set(),如本教程所示:
http://makeseleniumeasy.com/2020/05/27/threadlocal-static-webdriver-for-parallel-execution/
我注意到与您的代码相关的多个问题。
- 您正在使用
ThreadLocal.withInitial()
。理想情况下,这应该在实例化driver
线程局部静态变量时定义。
所以不用
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
应该是
private static final ThreadLocal<WebDriver> driver = ThreadLocal.withInitial(() -> {
return null; //Your supplier goes here.
});
您的继承层次结构明显混乱(很有可能您正在尝试创建一个简单的示例并且可能省略了继承层背后的细节),但是它不清楚为什么你所有的页面对象 classes 扩展
WorldHelper
您在 class 级别有多个语句,例如这样。这些字段级初始化的问题是它们在构造对象时被调用。因此,如果对象是在不同的线程中构建的,那么您 运行 就会遇到为该线程触发 WebDriver 初始化的问题。最终结果:您有很多不断打开的 ghost 浏览器实例,但没有针对它们的 selenium 操作。
private final WebDriver driver = WebDriverFactory.getDriver();
在使用 WebDriver 管理的 ThreadLocal
变体时,您需要确保您的调用始终来自您的步骤定义,而不是来自构造函数或来自 class 级别字段初始化,例如以上。
这是您需要执行的修复列表。
删除代码中出现的所有
private final WebDriver driver = WebDriverFactory.getDriver();
。不需要它们。重构您的 WebDriverFactory class 如下所示(为简洁起见,我删除了所有注释掉的代码)
public class WebDriverFactory {
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static void setDriver(String browser) {
RemoteWebDriver rwd;
switch (browser) {
case "chrome":
WebDriverManager.chromedriver().setup();
rwd = new ChromeDriver(BrowserOptions.getChromeOptions());
break;
case "fireFox":
WebDriverManager.firefoxdriver().setup();
rwd = new FirefoxDriver(BrowserOptions.getFirefoxOptions());
break;
default:
throw new IllegalStateException("Unexpected value: " + browser);
}
driver.set(Objects.requireNonNull(rwd));
prepareBrowser();
}
private static void prepareBrowser(){
getDriver().manage().window().maximize();
getDriver().manage().deleteAllCookies();
getDriver().manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
getDriver().manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
}
public static WebDriver getDriver(){
return Objects.requireNonNull(driver.get());
}
public static void closeBrowser() {
getDriver().manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
getDriver().close();
getDriver().quit();
}
}
- 因为你的所有页面 classes 似乎都是从
WorldHelper
扩展而来的,所以添加一个 getter 方法,如下所示(或)确保你的任何地方都没有页面 classes 你有一个WebDriver
字段。每当您需要获取 WebDriver 实例时,您应该直接通过WebDriverFactory.getDriver()
(或)通过 getter 方法(例如下面在您的WorldHelper
或任何基础 class 你正在创作。
protected WebDriver getDriver() {
return WebDriverFactory.getDriver();
}
一旦您解决了上述问题,您应该就没事了,应该不会看到任何空白浏览器 windows 打开。
注意:请在 GitHub 上清理您的项目。我注意到其中有一些云服务提供商凭证(可能是真实的凭证,也可能是假的。我不知道)