【问题标题】:Selenium test is overwritten by the second session while the parallel executionSelenium 测试在并行执行时被第二个会话覆盖
【发布时间】:2019-02-04 16:30:47
【问题描述】:

在通过 Saucelabs 执行跨浏览器 selenium java 测试时,我需要一些帮助来解决问题。我正在对从 Jenkins 作业中选择的 2 个浏览器运行一项测试。

在执行时,一项测试通过了另一项(类似的)测试失败并出现错误:

504 网关超时

。服务器没有及时响应。 所以它无法进入下一步。

似乎一个测试打断了另一个。两个测试都在各自的隧道和线程下运行。

Aug 30, 2018 7:17:44 AM org.openqa.selenium.remote.ProtocolHandshake 
createSession
INFO: Detected dialect: OSS
30-08-2018 07:17:49.235 [TestNG-PoolService-0] INFO  
[com.***.tests.TestBase_Local:96] - Open a site URLDriver: RemoteWebDriver: 
chrome on XP (4f5a5d685f4c44c9a5864e91cb8f11e9)
Driver: RemoteWebDriver: chrome on XP (4f5a5d685f4c44c9a5864e91cb8f11e9)
thread id:14 Timestamp :2018-08-30T07:17:49.370
30-08-2018 07:17:51.912 [TestNG-PoolService-0] INFO  
[com.**.tests.TestBase_Local:35] - Select 'No thanks' on the popup

Aug 30, 2018 7:17:53 AM org.openqa.selenium.remote.ProtocolHandshake 
createSession
INFO: Detected dialect: OSS
30-08-2018 07:17:58.886 [TestNG-PoolService-1] INFO  
[com.**.tests.TestBase_Local:96] - Open a site URLDriver: 
RemoteWebDriver: MicrosoftEdge on ANY (c6978c03531d408485588ba501ff0589)
Driver: RemoteWebDriver: MicrosoftEdge on ANY 
(c6978c03531d408485588ba501ff0589)
thread id:15 Timestamp :2018-08-30T07:17:58.887
30-08-2018 07:18:03.406 [TestNG-PoolService-1] INFO  
[com.**.tests.TestBase_Local:35] - Select 'No thanks' on the popup
30-08-2018 07:18:05.337 [TestNG-PoolService-1] INFO  
[com.**.tests.TestBase_Local:38] - Search by input

分享代码:

public class Search extends RemoteTestBase {

    @Test(dataProvider = "browsers")
    public void SolrSearchTest(String browser, String version, String os, Method method) throws Exception {
        this.createRemoteDriver(browser, version, os, method.getName());
        System.out.println("Driver: " + driver.toString());

        Application app = new Application(driver);

        ConfigFileReader configRead = new ConfigFileReader();

        WebDriverWait wait = new WebDriverWait(driver,100);

        app.homePage().SelectNoThanks();
        Log.info("Select 'No thanks' on the popup");

        app.searchField().SearchBy(configRead.SearchInput());
        Log.info("Search by input");
    }
}

扩展的 RemoteTestBase 类:

public class RemoteTestBase {

    public WebDriver driver;
    private static String baseUrl;
    RandomDataSelect randomuser;
    private PropertyLoader propertyRead;
    public Logger Log = Logger.getLogger(TestBase_Local.class.getName());

    private static final String SAUCE_ACCESS_KEY = System.getenv("SAUCE_ACCESS_KEY");
    private static final String SAUCE_USERNAME = System.getenv("SAUCE_USERNAME");

    @BeforeMethod
    @DataProvider(name = "browsers", parallel = true)
    public static Object[][] sauceBrowserDataProvider(Method testMethod) throws JSONException {
        String browsersJSONArrayString  = System.getenv("SAUCE_ONDEMAND_BROWSERS");
        System.out.println(browsersJSONArrayString);
        JSONArray browsersJSONArrayObj = new JSONArray(browsersJSONArrayString);

        Object[][] browserObjArray = new Object[browsersJSONArrayObj.length()][3];
        for (int i=0; i < browsersJSONArrayObj.length(); i++) {
            JSONObject browserObj = (JSONObject)browsersJSONArrayObj.getJSONObject(i);
            browserObjArray[i] = new Object[]{ browserObj.getString("browser"), browserObj.getString("browser-version"), browserObj.getString("os")};
        }
        return browserObjArray;
    }

    void createRemoteDriver(String browser, String version, String os, String methodName) throws Exception {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        Class<? extends RemoteTestBase> SLclass = this.getClass();
        capabilities.setCapability("browserName", browser);
        if (version != null) {
            capabilities.setCapability("browser-version", version);
        }
        capabilities.setCapability("platform", os);

        capabilities.setCapability("name", SLclass.getSimpleName());

        capabilities.setCapability("tunnelIdentifier", "***");

        driver = (new RemoteWebDriver(new URL("http://" + SAUCE_USERNAME + ":" + SAUCE_ACCESS_KEY + "@ondemand.saucelabs.com:80/wd/hub"), capabilities));

        randomuser = new RandomDataSelect();
        propertyRead = new PropertyLoader();
        baseUrl = propertyRead.getProperty("site.url");
        getURL();
    }

    private void getURL () {
        driver.get(baseUrl);
        driver.manage().timeouts().implicitlyWait(40, TimeUnit.SECONDS);
        this.annotate("Visiting HDSupply page..." + driver.toString());
        Log.info("Open a site URL" + "Driver: " + driver.toString());
    }

    private void printSessionId() {
        String message = String.format("SauceOnDemandSessionID=%1$s job-name=%2$s",(((RemoteWebDriver) driver).getSessionId()).toString(), "some job name");
        System.out.println(message);
    }

    @AfterMethod(description = "Throw the test execution results into saucelabs")
    public void tearDown(ITestResult result) throws Exception {
        ((JavascriptExecutor) driver).executeScript("sauce:job-result=" + (result.isSuccess() ? "passed" : "failed"));
        printSessionId();
        driver.quit();
    }

    void annotate(String text) {
        ((JavascriptExecutor) driver).executeScript("sauce:context=" + text);
    }
}

套件.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Tests Suite" verbose="4" parallel="tests" data-provider-thread-count="2">
    <test name="AllTests" parallel="methods">
        <classes>
            <class name="com.***.tests.Search"/>
        </classes>
    </test>
</suite>

项目信息:java、selenium、testng、maven、saucelabs、jenkins

【问题讨论】:

    标签: java selenium cross-browser testng saucelabs


    【解决方案1】:

    问题在于您的测试代码。它肯定与您的 @Test 方法之间的竞争条件有关 [您的 parallel="true" 在您的 @DataProvider 注释和 parallel="methods" ]

    您需要重构您的代码,使您的 driver 对象是线程安全的。

    您可以使用的一些方法是:

    • WebDriver 的 ThreadLocal 变体。
    • 创建您的 webdriver 实例并将其作为属性注入到 ITestResult 对象中。

    下面的示例展示了如何使用ThreadLocal 变体使您的代码线程安全

    import java.net.URL;
    import java.util.concurrent.TimeUnit;
    import org.openqa.selenium.remote.DesiredCapabilities;
    import org.openqa.selenium.remote.RemoteWebDriver;
    import org.testng.ITestResult;
    import org.testng.annotations.AfterMethod;
    
    public class RemoteTestBase {
    
      public static final ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<>();
      private static String baseUrl;
      private static final String SAUCE_ACCESS_KEY = System.getenv("SAUCE_ACCESS_KEY");
      private static final String SAUCE_USERNAME = System.getenv("SAUCE_USERNAME");
    
      void createRemoteDriver(String browser, String version, String os, String methodName)
          throws Exception {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        Class<? extends RemoteTestBase> SLclass = this.getClass();
        capabilities.setCapability("browserName", browser);
        if (version != null) {
          capabilities.setCapability("browser-version", version);
        }
        capabilities.setCapability("platform", os);
        capabilities.setCapability("name", SLclass.getSimpleName());
        capabilities.setCapability("tunnelIdentifier", "***");
    
        URL url = new URL(
            "http://" +
                SAUCE_USERNAME + ":" +
                SAUCE_ACCESS_KEY + "@ondemand.saucelabs.com:80/wd/hub");
    
        RemoteWebDriver rwd = new RemoteWebDriver(url, capabilities);
        driver.set(rwd);
        getURL();
      }
    
      protected static RemoteWebDriver getDriver() {
        return driver.get();
      }
    
      private void getURL() {
        getDriver().get(baseUrl);
        getDriver().manage().timeouts().implicitlyWait(40, TimeUnit.SECONDS);
        this.annotate("Visiting HDSupply page..." + driver.toString());
      }
    
      private void printSessionId() {
        String message = String.format("SauceOnDemandSessionID=%1$s job-name=%2$s",
                getDriver().getSessionId(), "some job name");
        System.out.println(message);
      }
    
      @AfterMethod(description = "Throw the test execution results into saucelabs")
      public void tearDown(ITestResult result) {
        String txt = "sauce:job-result=" + (result.isSuccess() ? "passed" : "failed");
        getDriver().executeScript(txt);
        printSessionId();
        getDriver().quit();
      }
    
      void annotate(String text) {
        getDriver().executeScript("sauce:context=" + text);
      }
    }
    

    您的所有子类都将尝试通过getDriver() 方法访问RemoteWebDriver 对象。

    需要注意的是,您的@BeforeMethod 需要调用createRemoteDriver(),以便RemoteWebDriver 对象被实例化并推送到ThreadLocal 上下文中,该上下文将是有效的并且可以在@Test 方法中访问。

    规则始终为@BeforeMethod(发生驱动程序实例化)>@Test(驱动程序被消耗>@AfterMethod(应该发生驱动程序清理)。这是RemoteWebDriver 对象在@ 中有效的唯一组合987654340@ 上下文。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-27
      • 2021-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多