Drukarnia.BLOG

Fluent wait або впевнене очікування та як його їсти

Цього разу буду розглядату впевнене очікування, хоча його називають ще інколи плаваючим, вільним або ще якось, втім називати в рамках цієї статті буду його впевненим. На жаль в більшості статей про очікування даний тип згадується досить коротко, що особисто мені не дуже подобається тому вважаю, що оминути його існування все-таки було б неправильно, хоча б з таких причи: воно існує, воно досить потужне, воно є базою для явного очікування, а також його можна використати, коли інші очікування не підходять.

Почну з можливостей, які дає цей тип очікуваня:

  • Керування часом очікування

  • Керування інтервалом між опитуваннями

  • Ігнорування винятків (NoSuchElementExceptions, ElementNotInteractableException і т.д.)

А ось і приклад такого очікування:

Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
  .withTimeout(Duration.ofSeconds(30)) //Встановлює час очікування 30 секунд
  .pollingEvery(Duration.ofSeconds(5)) //Встановлює інтервал опитування 5 секунд
  .ignoring(NoSuchElementException.class); //Визначає тип винятку, який буде ігноруватися

WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
  @Override
  public WebElement apply(WebDriver driver) {
    return driver.findElement(By.id("foo"));
  }
});

Умовно можемо розділити його на дві частини:

  1. Налаштування очікування ( Wait<Webdriver> wait = ... )

  2. Виконання самого очікування (wait.until(... ).

З назв методів можна зрозуміти, що вони виконують, а якщо ні, то пан автор додав там коментарі. А ось друга частина буде трішки цікавішою. Що ж, аргументом до методу until є функція, яка й буде виконувати очікування. Варто пам’ятати, що опис логіки потрібно робити в тілі функції apply. А от те, як працює все це під капотом — є тим, що потребує опису в окремому пості, а в цьому хочеться описати загалом, що це таке і як його з’їсти. Також варто додати, що запис другої частини може мати дещо інший вигляд, якщо використовувати лямбди:

WebElement foo = wait.until(driver -> driver.findElement(By.id("foo")));

Може здатися, що щоразу писати стіну коду, коли потрібно якесь очікування з різними налаштуваннями не надто зручно, втім можна зробити метод, який буде викликатися, замість постійного повтору коду:

private WebElement fluentWaitMethod(Duration timeout, By by) {
  return new FluentWait<WebDriver>(driver)
      .withTimeout(timeout)
      .ignoring(NoSuchElementException.class)
      .until(innerDriver -> innerDriver.findElement(by));
}

Можна помітити, що опущено налаштування інтервалу між опитуваннями і це не викличе помилки, оскільки воно має значення за замовчуванням, яке дорівнює 500мс, як і значення за замовчуванням для часу очікування.

Теорія — це, звісно, добре, а тепер варто перейти до практичного застосування і для цього використаю код з попередньої статті про неявне очікування:

/*
* Старий варіант
*/
WebElement h1 = driver.findElement(By.tagName("h1"));
System.out.println("Header text: " + h1.getText()); //Header text: Implicit Wait Example

WebElement firstParagraph = driver.findElement(By.tagName("p"));
System.out.println("First paragraph text: " + firstParagraph.getText()); //First paragraph text: This loads with page

WebElement clickMe = driver.findElement(By.id("addNewContent"));
System.out.println("Button text: " + clickMe.getText()); //Button text: ClickMe

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
WebElement secondParagraph = driver.findElement(By.id("postponedForFiveSeconds"));
System.out.println("Second paragraph text: " + secondParagraph.getText());//Second paragraph text: Loaded after 5 seconds.

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(11));
WebElement thirdParagraph = driver.findElement(By.id("postponedForElevenSeconds"));
System.out.println("Third paragraph text: " + thirdParagraph.getText());//Third paragraph text: Loaded after 11 seconds.

clickMe.click();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
WebElement fourthParagraph = driver.findElement(By.id("dynamicElement"));
System.out.println("Fourth paragraph text: " + fourthParagraph.getText());//Fourth paragraph text: Created at: 3:30:01 AM

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0));//Вимикає неявні очікування

А тепер перепишу його з використанням впевненого очікування:

/*
* З використанням fluent wait
*/

WebElement h1 = driver.findElement(By.tagName("h1"));

System.out.println("Header text: " + h1.getText()); //Header text: Implicit Wait Example

WebElement firstParagraph = driver.findElement(By.tagName("p"));
System.out.println("First paragraph text: " + firstParagraph.getText()); //First paragraph text: This loads with page

WebElement clickMe = driver.findElement(By.id("addNewContent"));
System.out.println("Button text: " + clickMe.getText()); //Button text: ClickMe

WebElement secondParagraph = fluentWaitMethod(Duration.ofSeconds(5), By.id("postponedForFiveSeconds"));
System.out.println("Second paragraph text: " + secondParagraph.getText());//Second paragraph text: Loaded after 5 seconds.

WebElement thirdParagraph = fluentWaitMethod(Duration.ofSeconds(11), By.id("postponedForElevenSeconds"));
System.out.println("Third paragraph text: " + thirdParagraph.getText());//Third paragraph text: Loaded after 11 seconds.

clickMe.click();
WebElement fourthParagraph = fluentWaitMethod(Duration.ofSeconds(5), By.id("dynamicElement"));
System.out.println("Fourth paragraph text: " + fourthParagraph.getText());//Fourth paragraph text: Created at: 3:30:01 AM

Отже, можна побачити, що в такому варіанті ми позбавилися постійних маніпуляцій із неявним очікуванням (driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));), а з використанням написаного методу fluentWaitMethod і зменшується загальна кількість коду через відсутність його повторювання, що одночасно покращує читабельність коду.

До речі, якщо розглянути умовну другу часнину очікування:

WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
  @Override
  public WebElement apply(WebDriver driver) {
    return driver.findElement(By.id("foo"));
  }
});

, то можна зрозуміти чому поєднання неявного очікування з іншими — не бажане, оскільки воно буде впливати на логіку, яку було описано в коді, збільшуючи час очікування описаного в тілі методу apply.

Тако ж було б добре навести приклад використання не тільки пошуку елементів, але й якоїсь взаємодії. Отже допустимо, що потрібно натиснути кнопку, яка перші 2 секунди перекривається якимось іншим елементом. Якщо спробувати одразу це зробити, то ймовірно буде викинутий виняток ElementClickInterceptedException, тому можна створити таке очікування:

Wait<WebDriver> wait =
    new FluentWait<WebDriver>(driver)
        .withTimeout(Duration.ofSeconds(3))
        .ignoring(ElementClickInterceptedException.class, NoSuchElementException.class);

wait.until(
    new Function<WebDriver, Boolean>() {
      public Boolean apply(WebDriver driver) {
        driver.findElement(by).click();
        return Boolean.TRUE;
      }
    });

В даному випадку повертати щось з функції при успішному кліку є обов’язково, оскільки метод until очікує, що з функції в тілі метода apply повернеться щось типу Boolean, якщо по-простому, адже детальний механізм роботи цього заслуговою більш детального розгляду в окремому пості, як пан автор казав раніше.

І на завершення хотів би зробити такі висноски про впевнене очікування(fluent wait):

  • Дозволяє налаштовувати такі параметри:

    • Час очікування

    • Інтервал між пошуком елементів

    • Типи винятків, що ігноруюються

  • Дозволяє описувати власну логіку очікування

  • Дозволяє налаштовувати очікування для кожного елементу окремо

  • Достатньо потужний інструмент для використання в тих місцях, де інші типи очікування не підходять

Сподіваюся мені вдалося розкрити дану тему детальніше, аніж це роблять інші та й взагалі маю надію, що даний матеріал був корисним та цікавим для прочитання. Коментарі, критика і т.д. — вітаються.

Articles about local business and interesting people:

Share your ideas in a new publication.
We are waiting for your longread!
Василь Побережник

Василь Побережник

@VasylPoberezhnyk

AQA | Фотограф-ентузіаст

5Longreads
759Views
4Followers
Support
On Drukarnia since August 29 2023

More from the author

You may also be interested in:

Comments (0)

Support the author first.
Write a comment!

You may also be interested in: