Написать пост

Способы ожидания в Java и Selenium

Аватар Типичный программист

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

Рассказывает Валерия, QA Automation Engineer в Noveo

Ожидания — не самый приятный процесс, где бы он ни происходил. Ожидания в автотестах — отдельная боль. Избавиться от них невозможно, но есть способы организовать их работу в более удобной форме, нежели Thread.sleep().

Чтобы собрать вместе и систематизировать информацию по этой теме, рассмотрю все варианты написания ожиданий для автоматизированного тестирования UI посредством Java и Selenium, с которыми мне приходилось работать, а именно ожидания, предоставляемые Selenium, возможность написания собственных ожиданий и библиотека Awaitility.

Selenium waiting methods

Implicit Wait

Implicit Wait, или неявное ожидание, — пожалуй, самый популярный способ ожидания в Selenium благодаря своей легкости в использовании.

Чтобы использовать Implicit Wait в автотестестах, достаточно:

  • установить его всего 1 раз,
  • указать вручную лимит ожидания.

После того, как команда исполнится, Implicit Wait будет действовать на протяжении всего пробега автотестов и ожидать указанное время прежде, чем выбросить NoSuchElementException (или не выбрасывать, если необходимый элемент на странице найден). Не устанавливать Implicit Wait равносильно нулевому лимиту времени, и исключение пробросится сразу.

Чтобы установить Implicit Wait, необходимо написать всего одну строку после установки драйвера, и таким образом мы установим лимит ожидания 10 секунд:

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

Implicit Wait можно использовать для:

  • ожидания полной загрузки страницы — pageLoadTimeout();
  • ожидания появления элемента на странице — implicitlyWait();
  • ожидания выполнения асинхронного запроса — setScriptTimeout();

Установка использования неявного ожидания будет выглядеть следующим образом:

			@BeforeMethod (alwaysRun = true)
public void setUpDriver() {

//Set up driver
    ChromeOptions options = new ChromeOptions();
    WebDriverManager.chromedriver().setup();
    driver = new ChromeDriver(options);

//Set implicit wait:
//wait for WebElement
    driver.manage().timeouts().implicitlyWait(5000,
TimeUnit.MILLISECONDS);

//wait for loading page
    driver.manage().timeouts().pageLoadTimeout(10000,
TimeUnit.MILLISECONDS);

//wait for an asynchronous script to finish execution
    driver.manage().timeouts().setScriptTimeout(5000,
TimeUnit.MILLISECONDS);
}

@Test (description = "Open url")
public void openUrl() {
//Open browser
    driver.manage().window().setSize(new Dimension(1280, 970));
//Get url
    driver.get("https://some_site.com");
//Search an element
    WebElement element = driver.findElement(By.id("some-id"));
    Assert.assertTrue(element.isDisplayed());

}

@AfterMethod (alwaysRun = true)
public void closeBrowser() {
//Close browser
    driver.quit();
}
		

 

Explicit Wait

Explicit wait, или явное ожидание, чаще используется для ожидания определенного условия, которое должно быть выполнено прежде, чем тест пойдет дальше.

О явном ожидании стоит помнить следующие вещи:

  • ожидание сработает именно там, где оно указано;
  • как и неявному ожиданию, ему необходимо указать лимит времени;
  • ожидает выполнения необходимого условия;
  • ждет завершения Ajax request.

Использовать явное ожидание можно через WebDriverWait. Инициализация будет происходить следующим образом:

WebDriverWait wait = new WebDriverWait(driver,10);

где driver является референсом к нашему используемому WebDriver, а число 10 — TimeOut в секундах.

В тесте само ожидание уже будет выглядеть примерно так:

			wait.until(ExpectedConditions.invisibilityOfElementLocated
             (By.cssSelector("element"));
		

Класс ExpectedConditions предоставляет нам ряд вещей, которых мы можем дожидаться во время пробега теста. Они все достаточно легко читаемы, что облегчит жизнь как вам, так и людям, которым, возможно, придется поддерживать ваши автотесты.

Вот полный список всего, что вам предлагает подождать ExpectedConditions:

  • alertIsPresent()
  • elementSelectionStateToBe()
  • elementToBeClickable()
  • elementToBeSelected()
  • frameToBeAvaliableAndSwitchToIt()
  • invisibilityOfTheElementLocated()
  • invisibilityOfElementWithText()
  • presenceOfAllElementsLocatedBy()
  • presenceOfElementLocated()
  • textToBePresentInElement()
  • textToBePresentInElementLocated()
  • textToBePresentInElementValue()
  • titleIs()
  • titleContains()
  • visibilityOf()
  • visibilityOfAllElements()
  • visibilityOfAllElementsLocatedBy()
  • visibilityOfElementLocated()

В тесте создание и использование неявного ожидания будут выглядеть следующим образом:

			private WebDriver driver;
private WebDriverWait wait;
private WebElement exampleElement;

@BeforeMethod(alwaysRun = true)
public void setUpDriver() {
//Set up driver
    ChromeOptions options = new ChromeOptions();
    WebDriverManager.chromedriver().setup();
    driver = new ChromeDriver(options);
//Set explicit wait
    wait = new WebDriverWait(driver, 10);
}

@Test(description = "Open url")
public void openUrl() {
//Open browser
    driver.manage().window().setSize(new Dimension(1280, 970));
//Get url
    driver.get("https://some-site.com");

//Wait until blockUI disappear
    wait.until(ExpectedConditions.invisibilityOfElementLocated
              (By.cssSelector("element")));
    wait.until(ExpectedConditions.invisibilityOf(exampleElement));

//Search an element
    WebElement element = driver.findElement(By.id("some-id"));
    Assert.assertTrue(element.isDisplayed());

//Click element
    element.click();

//Wait until alert is present
    wait.until(ExpectedConditions.alertIsPresent());
    driver.switchTo().alert().accept();

}

@AfterMethod(alwaysRun = true)
public void closeBrowser() {
//Close browser
    driver.quit();
}
		

 

Разница между Implicit и Explicit Wait

Способы ожидания в Java и Selenium 1

Написание собственных ожиданий

Ожидания Selenium не всегда способны удовлетворить потребности тестировщика. В таких случаях мы можем сами написать методы, которые удержат автотесты от падения. Работать кастомные ожидания будут по тому же принципу, что и Explicit Wait, т.е. срабатывать в той части теста, в которой указаны, а условие, которого необходимо дождаться, нужно написать самим как некий аналог класса ExpectedConditions. Хранить ли свои условия в отдельном классе или в том же, где и написанный способ ожидания, и нужно ли разделять метод ожидания и условие или достаточно будет оставить их прописанными в едином методе, зависит от условий проекта и предпочтений тестировщика.

Чтобы написать ожидание самим, потребуется не так много: старый добрый цикл while и System.currentTimeMillis().

Выглядеть это может примерно таким образом:

			public static void waitForInvisibilityOfElement(WebElement element) {
    float waitingTime = 0;

//Start loading time of waiting for invisibility of element
    startLoadingTime = System.currentTimeMillis();

    while (element.isDisplayed()){

//If element is still presented, verify if the
//maximum of waiting time is reached

        if(waitingTime <= MAX_WAITING_TIME) {
//Update waiting time
            waitingTime = System.currentTimeMillis() - startLoadingTime;

        }else{
            System.out.println("Condition wasn't executed with time limit");
            break;
        }

    }
//If the element disappeared, log the loading time of the

    if(!element.isDisplayed()) {
        System.out.println("Condition was executed in "
                                + waitingTime +
                                " seconds");
    }
}
		

 

Условие, которое мы ждем в этом примере — element.isDisplayed(),  его можно вынести в отдельный метод, возвращающий boolean, и таким образом сам метод ожидания можно прописать единожды и просто передавать туда разные условия в формате boolean.

Awaitility library

Пожалуй, лучшее, ну или, по крайней мере, мое любимое, решение проблемы ожиданий. Awaitility применима не только в автоматизации тестирования, однако я ограничусь рассмотрением ее использования на примерах тестов. Эта библиотека упростит вам жизнь при написании собственных ожиданий; все, что сказано про аналог кастомных ожиданий с Explicit Wait и ExpectedConditions, будет справедливо и здесь. То же касается и того, как вы предпочтете прописать условия, которых хотите дождаться. В рамках этой статьи рассмотрена только часть возможностей данной библиотеки. Если вам интересно почитать обо всех возможностях, можете ознакомиться с официальной документацией. Здесь я хочу обратить внимание на те моменты, которые мне показались максимально полезными, и просто поделиться тем, что есть такая замечательная штука, которая помогает стабилизировать автотесты.

Awaitility library позволит:

  • дождаться выполнения асинхронных запросов;
  • установить как максимум, так и минимум ожидаемого времени;
  • проигнорировать выпадающие исключения (в таком случае по истечению лимита времени будет выброшен ConditionTimeoutException);
  • использовать Assertion в качестве ожидаемого условия;
  • использовать polling.

Синтаксис довольно читабельный; Awaitility позволяет понять, что и куда вызывается для определения лимита времени, игнорирования исключений, где устанавливаем polling и чего ожидаем в финале нашего метода. Внутреннее написание ожидания может выглядеть так — остается только вызвать его в тесте. Хранить подобные методы стоит все же отдельно от тестов.

 

			public static void waitVisibleElement(WebElement we) {
    with().pollDelay(100, TimeUnit.MILLISECONDS).await().atMost
    (10, TimeUnit.SECONDS).until(we::isDisplayed);
}
		

Отдельным примером хочу показать ожидание Assertion, именно этот метод кажется мне особенно удобным, так как убивает сразу двух зайцев и позволяет не прописывать лишних строк кода:

 

			/**
 * Wait until two lists became equal
 * @param actualValues - actual values
 * @param requiredValues - expected values
 */
public static void waitOfAssertionForLists(List<WebElement>
       actualValues, List<string> requiredValues) {
    given().ignoreExceptions().await().atMost(10, TimeUnit.SECONDS)
            .untilAsserted(() -> Assert.assertEquals
            (getValuesFromDropDown(actualValues), requiredValues));
}

//Get text values from list of WebElements
private static List<string> getValuesFromDropDown(List wes){
    List actualValuesInDropDown = new ArrayList<>();
    for (WebElement we : wes) {
        if(we.isDisplayed() && we.getText().trim().length() > 0) {
            actualValuesInDropDown.add(we.getText().trim());
        }
    }
    return actualValuesInDropDown;
}
		

Это та информация, которой  я хотела поделиться об ожиданиях.

Более подробно вы можете ознакомиться с Awaitility по ссылке.

Следите за новыми постами
Следите за новыми постами по любимым темам
48К открытий49К показов