简体   繁体   English

具有并行线程的TestNG Selenium

[英]TestNG Selenium with parallel threads

I've seen many questions regarding this topic but none seem to pertain to what I am looking for. 我已经看到许多有关此主题的问题,但似乎都与我所寻找的问题无关。 I am trying to run parallel methods under a single class using the @BeforeMethod to start up the browser, and @AfterMethod to tear down the browser. 我试图使用@BeforeMethod启动浏览器,并使用@AfterMethod拆除浏览器,在单个类下运行并行方法。 For this example, let's just say it is a single class file that had several test methods inside of it. 对于此示例,我们只说它是一个单个类文件,其中包含多个测试方法。 I wish to run 4 of them at the same time in different browsers. 我希望在不同的浏览器中同时运行其中的4个。 We are using Groovy but a Java answer would suffice. 我们正在使用Groovy,但是Java答案就足够了。

XML: XML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="sampleSuite" thread-count="4" preserve-order="true" parallel="methods">
    <test name="sample">
        <classes>
            <class name="tests.some.class.MyClassFile"/>
        </classes>
    </test>
</suite>

DriverFactory class that is in charge of simply creating and returning a browser: 负责简单地创建和返回浏览器的DriverFactory类:

private static WebDriver createWebDriver() {
    String property = System.getProperty("driver", "chrome")
    def capabilities

    if (property == "chrome") {
        capabilities = DesiredCapabilities.chrome()


    } else if (property == "firefox") {
        System.setProperty("webdriver.gecko.driver", "/usr/local/Cellar/geckodriver/0.16.1/bin/geckodriver")
        capabilities = DesiredCapabilities.firefox()
    }

    def url = System.getProperty("seleniumServerUrl", "http://localhost:4444/wd/hub")

    if (!url) {
        throw new IllegalStateException("No 'seleniumServerUrl' system property set")
    }

    def webDriver = new RemoteWebDriver(new URL(url), capabilities)
    webDriver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS)
    webDriver.manage().window().setSize(new Dimension(1920,1080))

    Runtime.addShutdownHook {
        try {
            webDriver.quit()
        } catch (ignore) {}
    }
    webDriver
}

For threading, also in DriverFactory class: 对于线程化,同样在DriverFactory类中:

static final WebDriver createStackWebDriver(){
    ThreadLocal<WebDriver> webDriver = new ThreadLocal<WebDriver>(){
        protected WebDriver initialValue(){
            createWebDriver()
        }
    }
    webDriver.get()
}

Now I have a TestPlatform class that is the central "set up and tear down" piece of the architecture. 现在,我有一个TestPlatform类,它是体系结构的中心“设置和拆除”部分。 All of my class files that contain the @Test methods extends this TestPlatform class: 我所有包含@Test方法的类文件都扩展了此TestPlatform类:

class TestPlatform {

    WebDriver webDriver

    @BeforeMethod
    void startUpWebBrowser(){
        webDriver = DriverFactory.createStackWebDriver()
    }

    @AfterMethod
    void quitWebDriver(){
        webDriver.quit()
    }
}

When we run the XML file, what we see is 4 instances of startUpWebBrowser() , one out of the 4 would "pass" and continue the test, and it will promptly open up 4 methods out of one instance of startUpWebBrowser() , while the other 3 just stay "hanging". 当我们运行XML文件时,我们看到的是startUpWebBrowser() 4个实例,这4个实例中的1个将“通过”并继续测试,它将立即从startUpWebBrowser()的一个实例中打开4个方法,而其他3个保持“挂起”状态。 We need it to open up one browser in each startUpWebBrowser() instance, instead of 4 browsers in one instance. 我们需要它在每个startUpWebBrowser()实例中打开一个浏览器,而不是在一个实例中打开4个浏览器。

Our current workaround is to wrap EACH @Test method inside its own class, and change the @BeforeMethod into @BeforeClass as well as the afters. 我们当前的解决方法是将EACH @Test方法包装在其自己的类中,并将@BeforeMethod更改为@BeforeClass以及afters。 This somehow works but is extremely ugly on the reporting side of it, and there are other few drawbacks to this way. 这在某种程度上是可行的,但是在报告方面却非常丑陋,而且这种方式还有其他一些缺点。

Is there any solution to this issue without doing an overhaul of the current architecture? 如果不对当前体系结构进行彻底检查,是否可以解决此问题? Am I just missing something very simple? 我只是想念一些简单的东西吗?

The problem lies in your Test class TestPlatform wherein you are saving a webdriver instance variable into a class level data member WebDriver webDriver . 问题出在您的Test类TestPlatform其中您将一个webdriver实例变量保存到一个类级别的数据成员WebDriver webDriver

So when two or more @Test methods run in parallel, all of them are literally trying to assign the webdriver instance to the same webDriver instance. 因此,当两个或多个@Test方法并行运行时,所有这些方法实际上都是在尝试将webdriver实例分配给相同的webDriver实例。

Please fix it as below. 请按照以下说明进行修复。

class TestPlatform {

    @BeforeMethod
    void startUpWebBrowser(){
        DriverFactory.createStackWebDriver()
        //Access webdriver instances only via DriverFactory.createStackWebDriver()
    }

    @AfterMethod
    void quitWebDriver(){
        DriverFactory.createStackWebDriver().quit()
    }
}

Seems like a ThreadLocal misusing. 似乎是ThreadLocal滥用。 I'd use the following snippet: 我将使用以下代码段:

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

@BeforeMethod
void startUpWebBrowser(){
    DRIVER_CONTAINER.set(createWebDriver());
}

@AfterMethod
void quitWebDriver(){
    ofNullable(getDriver()).ifPresent(WebDriver::quit);
    DRIVER_CONTAINER.remove();
}

public static WebDriver getDriver() {
    return DRIVER_CONTAINER.get();
}

Or just move driver management piece into IInvokedMethodListener . 或者只是将驱动程序管理块移到IInvokedMethodListener

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM