Параллельный запуск тестов в нескольких браузерах проблема с инстанс

Сейчас тесты бегают в параллели с одним выбранным браузером. Нужно эти же тесты запускать в нескольких браузерах одновременно.

Для этого был создан класс для унаследования, с desired capabilities и dataProvider, назовем его RemoteTestBase:

 public class RemoteTestBase {

public WebDriver driver;

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");

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();
configRead = new ConfigFileReader();
propertyRead = new PropertyLoader();
baseUrl = propertyRead.getProperty("site.url");

getURL();
}

  @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(script "sauce:context="+ text); } }

Сам тестовый класс выглядит как:

   public class Login extends RemoteTestBase {

   @Test (dataProvider = "browsers")
   public void LoginTest(String browser, String version, String os, Method method) throws Exception {

   this.createRemoteDriver(browser, version, os, method.getName());

   Application app = new Application (driver);

   app.login().login_as(configRead.getUsername, configRead.getPassword)

   }

При запуске данного теста в Saucelabs, в 2-х браузерах параллельно, происходит то, что оба теста якобы используют один инстанс app. И один из тестов - получает двойной ввод в поле логин-пароль, когда второй падает, не получая никакого ввода.

Помогите разобраться в чем же проблема.

Работаю на джава, с селениум вебдрайвер, testng, maven

Самое важное - как у вас объявлено поле driver. По хорошей традиции, именно это вы не показали. :slight_smile:

1 лайк

Показано в классе RemoteTestBase как public WebDriver driver. В тесте ЛогинТест драйвер не обьявлен

А, точно. С ним вроде всё ок, тогда надо смотреть, что там внутри Application и login_as()

1 лайк

Собственно вот что:

public class Application {
 private WebDriver driver;

 public Application(WebDriver driver)

{ this.driver = driver;
  WebDriverWait wait = new WebDriverWait(driver, 5)
}

public LoginElements login() 
 { 
 return new LoginElements(driver);
 }
}

Следующий Логин класс:

public class LoginElements{
 public WebDriver driver;

@FindBy(how= How.XPATH, using = "locator")
private WebElement logonID;

@FindBy(how= How.XPATH, using = "locator2")
private WebElement passwordID;

@FindBy(how= How.XPATH, using = "locator3")
private WebElement submit;

public void login_as(String sUsername, String sPassword)
{ 
logonID.sendKeys(sUsername);
passwordID.sendKeys(sPassword);
submit.click();
}

//Constructor 
public LoginElements(WebDriver driver)

{this.driver=driver;
 PageFactory.initElements(driver, this);
}

Тогда вообще ХЗ. Вроде классический пэдж обжект, должен переживать мультипоточность.

Могу предположить, что один браузер ворует фокус у другого. Они на одном экране (DISPLAY) запускаются? Нельзя настроить этот Saucelabs так, чтобы они на разных виртуальных экранах запускались?

P.S. Как обычно, рекомендую использовать Selenide - с ним ваш код станет короче в разы. Все эти PageFactory.initElements просто будут не нужны.

1 лайк

Они запускаются на одном облаке в сауслабсе путём использования Дженкинс job, но каждый тест открывается в отдельной сессии. Кстати, браузеры задаются именно в Дженкинс, тесты ранаются на сауслабс.
К сожалению, селенида не выход. Нужно придерживаться этой концепции.

Удалось отследить, что тест запускается в отдельном потоке thread id и имеет отдельный thread pool service. Сначала создаётся первая сессия, скажем в эдже, идёт выполнение теста. Параллельно , но с небольшой задержкой, запускается вторая сессия в хром браузере и именно она ломает выполнение предыдущей сессии.

Похоже, просто один браузер перехватывает фокус у другого. Дело вообще не в вашем коде. Это возможно?

Что насчёт этого - пробовали?

Они на одном экране (DISPLAY) запускаются? Нельзя настроить этот Saucelabs так, чтобы они на разных виртуальных экранах запускались?

Я думаю они запускаются каждый на своём экране. Вообще мнение, может ли ThreadLocal исправить ошибки?

Ах, это же TestNG! TestNG - зло, в следующем проекте не используйте его.

Давайте проверим гипотезу. Надо понять, это один и тот же инстанс класса RemoteTestBase или разные. Добавьте в класс RemoteTestBase строчку System.out.println(this); Если TestNG создаёт один инстанс для всех тестов, это всё объясняет (и да, ThreadLocal тут поможет, а ещё лучше - переход на JUnit, который такой ерундой не занимается).

вы правы, оба браузера возвращают один и тот же интсанс класса

Ну точно. Это зло№№бучий TestNG. Он создаёт один инстанс тест-класса для всех методов. А вот JUnit лапа, он создаёт новый инстанс для каждого следующего теста. В общем, да, вам поможет private static ThreadLocal. Но не забудьте его ещё и закрыть в @After методе.

P.S. А всё-таки, почему не Selenide? Он просто решает из коробки все эти проблемы. Вам бы вообще не пришлось мучаться со всем этим.

На моем проекте проще встретить слона, нежели переубедить менеджера и лида перейти на селениду. Это первый совет, который получаю от ребят)

Пробую обернуть в ThreadLocal, но пока не понятно как использовать и что собственно такое RemoteWebDriver get Driver() и как привязать этот драйвер к Пейдж классам и конструкторам (у меня в каждом Пейдж классе вызывается драйвер (private WebDriver driver) и его же использует конструктор)

И чем они аргументируют?

Просто вы сейчас начнёте делать всё то же самое, что там уже сделано. Наступать на те же грабли, делать те же ошибки. Зачем тратить время впустую?

У нас другие проекты на Селениум. Переходить на селениду специально никто не будет, тем более этот фрейморк используется уже для запуска тестов в одном браузере. Вот решили попробовать кросс браузерность. но пока одни проблемы с ним

В смысле? Так селенид как раз и предоставляет кроссбраузерность из коробки.

Да за то время, что вы тут вопросы писали и экспериментировали, вы бы уже сто раз перешли. Это ж как раз очень быстро делается.

вот по моему неплохая
https://www.swtestacademy.com/selenium-parallel-tests-grid-testng/

это конечно старый туториал но наверное неплохо для начала

@asolntsev объясните пожалуйста поч testng зло а junix X нет только пожалуйста не переводите на selenite - я почему то хочу пока без него обойтись

Так я же написал выше:

Ну точно. Это зло№№бучий TestNG. Он создаёт один инстанс тест-класса для всех методов. А вот JUnit лапа, он создаёт новый инстанс для каждого следующего теста.

Это, конечно, всего лишь одна из причин. Все перечислять - отдельная статья.

Спасибо всем, настроилась успешно безопасные потоки. Все работает благополучно!