Як я можу попросити Selenium-WebDriver почекати кілька секунд на Java?


100

Я працюю над Java Selenium-WebDriver. я додав

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

і

WebElement textbox = driver.findElement(By.id("textbox"));

оскільки мої програми потребують декількох секунд для завантаження інтерфейсу користувача. Тому я встановив 2 секунди неявно. але мені не вдалося знайти текстове поле елементів

Потім додаю Thread.sleep(2000);

Зараз це прекрасно працює. Який із них кращий спосіб?


Відповіді:


123

Ну, є два типи очікування: явне та неявне очікування. Ідея явного очікування є

WebDriverWait.until(condition-that-finds-the-element);

Поняття неявного очікування є

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

Ви можете отримати різницю в деталях тут .

У таких ситуаціях я вважаю за краще використовувати явне очікування ( fluentWaitзокрема):

public WebElement fluentWait(final By locator) {
    Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
            .withTimeout(30, TimeUnit.SECONDS)
            .pollingEvery(5, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class);

    WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver driver) {
            return driver.findElement(locator);
        }
    });

    return  foo;
};

fluentWaitфункція повертає знайдений веб-елемент. З документації оfluentWait : Впровадження інтерфейсу Wait, який може мати інтервал часу та час опитування, налаштований на льоту. Кожен екземпляр FluentWait визначає максимальну кількість часу для очікування умови, а також частоту перевірки стану. Крім того, користувач може налаштувати очікування для ігнорування конкретних типів винятків під час очікування, наприклад NoSuchElementExceptions під час пошуку елемента на сторінці. Деталі ви можете отримати тут

Використання fluentWait у вашому випадку має бути таким:

WebElement textbox = fluentWait(By.id("textbox"));

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


3
Це import com.google.common.base.Function;не такimport java.util.function.Function;
перевірив

не перевірив, фактично використовуючи intelij IDEA для розробки, він допомагає з автоматичним імпортом (при роботі над проектом на базі Maven)
eugene.polschikov

1
Я просто закликав це для інших, хто, можливо, цікавиться, що Functionбуло використано. Спочатку мені це не було очевидно. Дякую за відповідь.
перевірив

1
Або ви могли б просто використовувати лямбда - вираз: driver -> driver.findElement(locator). При використанні цього запиту про імпорт взагалі не знадобиться.
Thibstars

2
Тепер це: .withTimeout(Duration.ofSeconds(30)).pollingEvery(Duration.ofSeconds(5))замість: .withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
SuperRetro

16

Ця тема трохи старша, але я подумав, що я опублікую те, що зараз роблю (незавершена робота).

Хоча я все ще потрапляю в ситуації, коли система перебуває під великим навантаженням і коли я натискаю кнопку подання (наприклад, login.jsp), усі три умови (див. Нижче) повертаються true але наступна сторінка (наприклад, home.jsp) hasn ' t ще не почав завантажувати

Це загальний метод очікування, який включає список очікуваних умов.

public boolean waitForPageLoad(int waitTimeInSec, ExpectedCondition<Boolean>... conditions) {
    boolean isLoaded = false;
    Wait<WebDriver> wait = new FluentWait<>(driver)
            .withTimeout(waitTimeInSec, TimeUnit.SECONDS)
            .ignoring(StaleElementReferenceException.class)
            .pollingEvery(2, TimeUnit.SECONDS);
    for (ExpectedCondition<Boolean> condition : conditions) {
        isLoaded = wait.until(condition);
        if (isLoaded == false) {
            //Stop checking on first condition returning false.
            break;
        }
    }
    return isLoaded;
}

Я визначив різні багаторазові очікувані умови (три нижче). У цьому прикладі три очікувані умови включають document.readyState = 'завершено', немає "wait_dialog" і немає "spinners" (елементи, що вказують на дані асинхронізації, запитуються).

Лише перший може бути застосований до всіх веб-сторінок.

/**
 * Returns 'true' if the value of the 'window.document.readyState' via
 * JavaScript is 'complete'
 */
public static final ExpectedCondition<Boolean> EXPECT_DOC_READY_STATE = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        String script = "if (typeof window != 'undefined' && window.document) { return window.document.readyState; } else { return 'notready'; }";
        Boolean result;
        try {
            result = ((JavascriptExecutor) driver).executeScript(script).equals("complete");
        } catch (Exception ex) {
            result = Boolean.FALSE;
        }
        return result;
    }
};
/**
 * Returns 'true' if there is no 'wait_dialog' element present on the page.
 */
public static final ExpectedCondition<Boolean> EXPECT_NOT_WAITING = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        Boolean loaded = true;
        try {
            WebElement wait = driver.findElement(By.id("F"));
            if (wait.isDisplayed()) {
                loaded = false;
            }
        } catch (StaleElementReferenceException serex) {
            loaded = false;
        } catch (NoSuchElementException nseex) {
            loaded = true;
        } catch (Exception ex) {
            loaded = false;
            System.out.println("EXPECTED_NOT_WAITING: UNEXPECTED EXCEPTION: " + ex.getMessage());
        }
        return loaded;
    }
};
/**
 * Returns true if there are no elements with the 'spinner' class name.
 */
public static final ExpectedCondition<Boolean> EXPECT_NO_SPINNERS = new ExpectedCondition<Boolean>() {
    @Override
    public Boolean apply(WebDriver driver) {
        Boolean loaded = true;
        try {
        List<WebElement> spinners = driver.findElements(By.className("spinner"));
        for (WebElement spinner : spinners) {
            if (spinner.isDisplayed()) {
                loaded = false;
                break;
            }
        }
        }catch (Exception ex) {
            loaded = false;
        }
        return loaded;
    }
};

Залежно від сторінки, я можу використовувати один або всі:

waitForPageLoad(timeoutInSec,
            EXPECT_DOC_READY_STATE,
            EXPECT_NOT_WAITING,
            EXPECT_NO_SPINNERS
    );

Також є попередньо визначені очікувані умови в наступному класі: org.openqa.selenium.support.ui.ExpectedConditions


1
Чудова відповідь! Я ніколи не бачив, щоб хтось передавав елемент ExpectedCondition через конструктор методів. Яка чудова ідея. +1
djangofan

Джефф Вінсент, скажіть, будь ласка, чи буде ця сторінка зачекати 5 хвилин, якщо так, то, будь ласка, підкажіть, який параметр місця потрібно надіслати?
ханам

Це чудова відповідь. У мене є подібна функція, як ця, однак я повинен викликати її в кожній функції, щоб мій сценарій чекав, поки завантажується сторінка (спінер). Чи є спосіб, що я можу підключити цю функцію waitForSpinner мовчки до ImplicitWait, щоб мені не потрібно було викликати це кожен раз у своїй функції? Як і визначити функцію, підключіть її до водія один раз і піднімайте стрілу.
Bhuvanesh Mani

13

Якщо ви використовуєте webdriverJs (node.js),

driver.findElement(webdriver.By.name('btnCalculate')).click().then(function() {
    driver.sleep(5000);
});

Код, наведений вище, змушує браузера чекати 5 секунд після натискання кнопки.


18
Чому ви публікуєте це, коли питання стосується конкретно Java, а не вузла / JavaScript? Це так само поза темою, як відповідь, як це зробити в рубіні.
Thor84no

1
Рекомендація використовувати сон - це точно не вдале рішення
Кирило С.

@ Thor84no Переважно з того, що деякі з нас шукають веб-рішення, знаходять цю відповідь
Kitanga Nday,

10

Використання Thread.sleep(2000);- це безумовне очікування. Якщо ваш тест завантажується швидше, вам доведеться почекати. Тож в принципі використання implicitlyWait- це краще рішення.

Однак я не бачу, чому implicitlyWaitце не працює у вашому випадку. Ви міряли, чи findElementдійсно потрібно дві секунди, перш ніж кинути виняток. Якщо так, чи можете ви спробувати використати умовне очікування WebDriver, як описано у цій відповіді?


3

Мені подобається використовувати спеціальні умови. Ось код у Python:

def conditions(driver):
    flag = True
    ticker = driver.find_elements_by_id("textbox")
    if not ticker:
        flag = False
    return flag

... click something to load ...
self.wait = WebDriverWait(driver, timeout)
self.wait.until(conditions)

Щоразу, коли вам потрібно чекати, ви можете це зробити явно, перевіривши існування певного елемента (такий елемент може відрізнятися від сторінки до сторінки). find_elements_by_idсписок повернень - порожній чи ні, потрібно просто перевірити.


2

видається, що натискання блокується? - ось ще один спосіб почекати, якщо ви використовуєте WebDriverJS:

driver.findElement(webdriver.By.name('mybutton')).click().then(function(){
  driver.getPageSource().then(function(source) {
    console.log(source);
  });
});

Код вище чекає після натискання кнопки для завантаження наступної сторінки, а потім захоплює джерело наступної сторінки.


1
Що змушує код чекати завантаження наступної сторінки? Чи просто перший виклик у зворотному дзвінку - getPageSource?
Ізохронний

1

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

Ось мій код:

package beckyOwnProjects;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;

public class Flip {

    public static void main(String[] args) throws InterruptedException {
        WebDriver driver=new FirefoxDriver();
        driver.manage().window().maximize();
        driver.manage().timeouts().implicitlyWait(2, TimeUnit.MINUTES);
        driver.get("https://www.flipkart.com");
    WebElement ele=driver.findElement(By.cssSelector(".menu-text.fk-inline-block"));
    Actions act=new Actions(driver);
    Thread.sleep(5000);
    act.moveToElement(ele).perform();
    }

}

0

Іноді неявне очікування, здається, перекривається, а час очікування скорочується. [@ eugene.polschikov] мав гарну документацію щодо витів. У моєму тестуванні та кодуванні Selenium 2 я виявив, що неявні очікування хороші, але час від часу вам доведеться чекати явно.

Краще уникати прямого виклику нитки спати, але іноді це не дуже добре. Однак є й інші варіанти очікування, що надаються Selenium, які допомагають. waitForPageToLoad і waitForFrameToLoad виявилися особливо корисними.


0

Неявне зачекання: Під час нечіткого очікування, якщо веб-драйвер не зможе знайти його негайно через свою доступність, WebDriver чекатиме згаданого часу, і він не буде намагатися знову знайти елемент протягом зазначеного періоду часу. Як тільки зазначений час закінчиться, він спробує шукати елемент ще раз в останній раз перед викиданням виключення. За замовчуванням встановлено нуль. Як тільки ми встановимо час, веб-драйвер чекає періоду екземпляра об’єкта WebDriver.

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


0

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

Рішення полягає у тому, щоб уникнути використання driver.findElement та замінити його спеціальним методом, який використовує явне очікування неявно. Наприклад:

import org.openqa.selenium.NoSuchElementException;


public WebElement element(By locator){
    Integer timeoutLimitSeconds = 20;
    WebDriverWait wait = new WebDriverWait(driver, timeoutLimitSeconds);
    try {
        wait.until(ExpectedConditions.presenceOfElementLocated(locator));
    }
    catch(TimeoutException e){
        throw new NoSuchElementException(locator.toString());
    }
    WebElement element = driver.findElement(locator);
    return element;
}

Є додаткові причини, щоб уникнути неявного очікування, крім випадкових, випадкових збоїв (див. Це посилання ).

Ви можете використовувати цей метод "елемента" так само, як і драйвер.findElement. Наприклад:

    driver.get("http://yoursite.html");
    element(By.cssSelector("h1.logo")).click();

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

    public void pause(Integer milliseconds){
    try {
        TimeUnit.MILLISECONDS.sleep(milliseconds);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

0

Відповідь : зачекайте кілька секунд, перш ніж видимість елементів за допомогою Selenium WebDriver перейде до наведених нижче методів.

implicitlyWait () : екземпляр WebDriver зачекає до повного завантаження сторінки. Ви використовуєте 30 до 60 секунд, щоб дочекатися повного завантаження сторінки.

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

Явно зачекайте WebDriverWait () : екземпляр WebDriver дочекається повного завантаження сторінки.

WebDriverWait wait = new WebDriverWait(driver, 60);

wait.until(ExpectedConditions.visibilityOf(textbox));

driver.findElement(By.id("Year")).sendKeys(allKeys);

Примітка . Будь ласка, використовуйте ExplicitlyWait WebDriverWait () для обробки будь-якого конкретного WebElement.



-1

Я вважаю за краще, щоб наступний код зачекав 2 секунди.

for(int i=0; i<2 && driver.findElements(By.id("textbox")).size()==0 ; i++){
   Thread.sleep(1000);
}

-1
Thread.sleep(1000);

гірше: статичне очікування зробить тестовий сценарій повільніше.

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

це динамічне очікування

  • він діє до існування веб-драйвера або має рамки до терміну служби драйвера
  • ми також можемо неявно чекати.

Нарешті, те, що я пропоную

WebDriverWait wait = new WebDriverWait(driver,20);
wait.until(ExpectedConditions.<different canned or predefined conditions are there>);

з деякими заздалегідь заданими умовами:

isAlertPresent();
elementToBeSelected();
visibilityOfElementLocated();
visibilityOfAllElementLocatedBy();
frameToBeAvailableAndSwitchToIt();
  • Це також динамічне очікування
  • в цьому очікування буде лише через секунди
  • ми повинні використовувати явне очікування певного веб-елемента, на якому ми хочемо використовувати.

-4
Thread.Sleep(5000);

Це допомогло мені, але про виключення InterruptException потрібно подбати. Тож краще оточіть його спробуйте і ловити:

try {
    Thread.Sleep(5000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

АБО

Додати декларацію про кидки:

public class myClass {
    public static void main(String[] args) throws InterruptedException
    { ... }

Я вважаю за краще другий, оскільки потім можна використовувати sleep()стільки разів, скільки хоче, і уникати повторень tryта catchблокування кожного разу, де б sleep()це було використано.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.