Jak mogę poprosić Selenium-WebDriver, aby odczekał kilka sekund w Javie?

100

Pracuję na Java Selenium-WebDriver. dodałem

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

i

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

ponieważ załadowanie interfejsu użytkownika zajmuje mi kilka sekund. Więc ustawiłem 2 sekundy implicitwait. ale nie udało mi się zlokalizować pola tekstowego elementu

Następnie dodaję Thread.sleep(2000);

Teraz działa dobrze. Który z nich jest lepszy?

Książę
źródło

Odpowiedzi:

123

Cóż, istnieją dwa typy oczekiwania: jawne i niejawne oczekiwanie. Idea jawnego czekania jest taka

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

Pojęcie niejawnego oczekiwania to

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

Można uzyskać różnicę w szczegółach tutaj .

W takich sytuacjach wolałbym użyć jawnego czekania ( fluentWaitw szczególności):

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;
};

fluentWaitfunkcja zwraca znaleziony element sieciowy. Z dokumentacji na temat fluentWait: Implementacja interfejsu Wait, która może mieć skonfigurowany limit czasu i interwał odpytywania w locie. Każda instancja FluentWait definiuje maksymalny czas oczekiwania na warunek, a także częstotliwość sprawdzania warunku. Ponadto użytkownik może skonfigurować oczekiwanie, aby ignorować określone typy wyjątków podczas oczekiwania, takie jak NoSuchElementExceptions podczas wyszukiwania elementu na stronie. Szczegóły znajdziesz tutaj

Wykorzystanie fluentWaitw twoim przypadku jest następujące:

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

To podejście IMHO jest lepsze, ponieważ nie wiesz dokładnie, ile czasu czekać, aw interwale odpytywania możesz ustawić dowolną wartość czasu, przez którą obecność elementu zostanie zweryfikowana. Pozdrowienia.

eugene.polschikov
źródło
3
To import com.google.common.base.Function;nieimport java.util.function.Function;
sprawdziłem
haventchecked, faktycznie wykorzystując intelij IDEA do programowania, pomaga w automatycznym imporcie (podczas pracy w projekcie opartym na maven)
eugene.polschikov
1
Wołałem to tylko dla innych, którzy mogą się zastanawiać, co Functionzostało użyte. Na początku nie było to dla mnie oczywiste. Dziękuję za odpowiedź.
sprawdziłem
1
Lub może po prostu użyć wyrażenia lambda: driver -> driver.findElement(locator). Podczas korzystania z tego w ogóle nie potrzebujesz instrukcji importu.
Thibstars
2
Teraz jest: .withTimeout(Duration.ofSeconds(30)).pollingEvery(Duration.ofSeconds(5))zamiast: .withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
SuperRetro
16

Ten wątek jest nieco starszy, ale pomyślałem, że opublikuję to, co obecnie robię (w toku).

Chociaż nadal trafiam w sytuacje, w których system jest mocno obciążony i kiedy klikam przycisk przesyłania (np. Login.jsp), wszystkie trzy warunki (patrz poniżej) wracają, trueale następna strona (np. Home.jsp) nie t zacząłem jeszcze ładować.

Jest to ogólna metoda oczekiwania, która pobiera listę ExpectedConditions.

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;
}

Zdefiniowałem różne ExpectedConditions wielokrotnego użytku (trzy poniżej). W tym przykładzie trzy oczekiwane warunki to document.readyState = 'complete', brak „wait_dialog” i brak „spinners” (elementy wskazujące na dane asynchroniczne są żądane).

Tylko pierwszy z nich można zastosować ogólnie do wszystkich stron internetowych.

/**
 * 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;
    }
};

W zależności od strony mogę użyć jednego lub wszystkich z nich:

waitForPageLoad(timeoutInSec,
            EXPECT_DOC_READY_STATE,
            EXPECT_NOT_WAITING,
            EXPECT_NO_SPINNERS
    );

Istnieją również predefiniowane ExpectedConditions w następującej klasie: org.openqa.selenium.support.ui.ExpectedConditions

Jeff Vincent
źródło
1
Świetna odpowiedź! Nigdy nie widziałem, aby ktoś przekazał element ExpectedCondition za pośrednictwem konstruktora metody. Co za świetny pomysł. +1
djangofan
Jeff Vincent, czy możesz mi powiedzieć, czy ta strona będzie działać, aby strona odczekała 5 minut. Jeśli tak, to zasugeruj, który parametr miejsca należy wysłać?
khanam
To świetna odpowiedź. Mam podobną funkcję, jednak muszę ją wywoływać w każdej funkcji, aby mój skrypt czekał na załadowanie strony (spinner). Czy istnieje sposób, aby móc po cichu podłączyć tę funkcję waitForSpinner do ImplicitWait, aby nie musieć jej wywoływać za każdym razem w mojej funkcji? Podobnie jak zdefiniuj funkcję, podłącz ją raz do kierowcy i bum.
Bhuvanesh Mani
13

Jeśli używasz webdriverJs (node.js),

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

Powyższy kod powoduje, że przeglądarka czeka przez 5 sekund po kliknięciu przycisku.

Anup
źródło
18
Dlaczego miałbyś to publikować, skoro pytanie dotyczy konkretnie Javy, a nie węzła / JavaScript? To równie nie na temat, jak odpowiedź na pytanie, jak to zrobić w języku rubinowym.
Thor84no
1
zalecenie używania snu zdecydowanie nie jest dobrym rozwiązaniem
Kiril S.
@ Thor84no Głównie dlatego, że niektórzy z nas szukający rozwiązania internetowego znajdują tę odpowiedź
Kitanga Nday
10

Używanie Thread.sleep(2000);to bezwarunkowe czekanie. Jeśli twój test ładuje się szybciej, nadal będziesz musiał czekać. Więc w zasadzie używanie implicitlyWaitjest lepszym rozwiązaniem.

Jednak nie widzę powodu, dla którego implicitlyWaitnie działa w Twoim przypadku. Czy zmierzyłeś, czy findElementfaktycznie zajmuje to dwie sekundy przed rzuceniem wyjątku? Jeśli tak, czy możesz spróbować użyć warunkowego oczekiwania WebDriver, jak opisano w tej odpowiedzi?

Valentin
źródło
3

Lubię używać niestandardowych warunków. Oto kod w Pythonie:

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)

Zawsze, gdy musisz czekać, możesz to zrobić jawnie, sprawdzając istnienie określonego elementu (taki element może się różnić w zależności od strony). find_elements_by_idlista zwrotów - pusta czy nie, wystarczy sprawdzić.

simno
źródło
2

kliknięcie wydaje się blokować? - oto inny sposób czekania, jeśli używasz WebDriverJS:

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

Powyższy kod czeka po kliknięciu przycisku na załadowanie następnej strony, a następnie pobiera źródło następnej strony.

Dion
źródło
1
Co sprawia, że ​​kod czeka na załadowanie następnej strony? Czy po prostu pierwsze wywołanie w wywołaniu zwrotnym to getPageSource?
Izochroniczny
1

Niejawnie czekaj i Thread.sleep Oba są używane tylko do synchronizacji, ale różnica polega na tym, że możemy użyć Niejawnie poczekaj na cały program, ale Thread.sleep będzie działać tylko dla tego pojedynczego kodu. gdy za każdym razem Twoja strona internetowa będzie odświeżana, oznacza to, że użyj wątku. uśpiony w tym czasie .. będzie o wiele lepiej :)

Oto mój kod:

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();
    }

}
Beckham Vinoth
źródło
0

Czasami zdaje się, że niejawne oczekiwanie zostaje zastąpione, a czas oczekiwania zostaje skrócony. [@ eugene.polschikov] miał dobrą dokumentację na temat powodów. Podczas testowania i kodowania z Selenium 2 odkryłem, że niejawne oczekiwania są dobre, ale czasami trzeba czekać jawnie.

Lepiej jest unikać bezpośredniego wzywania wątku do snu, ale czasami nie ma na to dobrego sposobu. Istnieją jednak inne opcje oczekiwania dostępne w Selenium, które pomagają. waitForPageToLoad i waitForFrameToLoad okazały się szczególnie przydatne.

BadgerAndK
źródło
0

Niejawne oczekiwanie: Podczas niejawnego oczekiwania, jeśli sterownik sieciowy nie może go natychmiast znaleźć ze względu na jego dostępność, WebDriver będzie czekał przez wspomniany czas i nie będzie próbował ponownie znaleźć elementu w określonym przedziale czasu. Po upływie określonego czasu spróbuje przeszukać element jeszcze raz, po raz ostatni przed rzuceniem wyjątku. Domyślne ustawienie to zero. Po ustawieniu czasu sterownik sieciowy czeka na okres wystąpienia obiektu WebDriver.

Jawne oczekiwanie: może wystąpić wystąpienie, gdy załadowanie określonego elementu trwa dłużej niż minutę. W takim przypadku na pewno nie lubisz ustawiać ogromnego czasu na niejawne oczekiwanie, ponieważ jeśli to zrobisz, twoja przeglądarka będzie czekać w tym samym czasie na każdy element. Aby uniknąć takiej sytuacji, możesz po prostu ustawić oddzielny czas tylko dla wymaganego elementu. Postępując zgodnie z tym, domyślny czas oczekiwania przeglądarki byłby krótki dla każdego elementu i długi dla określonego elementu.

Abhishek Singh
źródło
0

Czasami niejawne oczekiwanie zawodzi, mówiąc, że element istnieje, ale tak naprawdę nie jest.

Rozwiązaniem jest uniknięcie używania driver.findElement i zastąpienie go metodą niestandardową, która niejawnie używa jawnego oczekiwania. Na przykład:

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;
}

Istnieją dodatkowe powody, dla których należy unikać niejawnego oczekiwania, inne niż sporadyczne, sporadyczne awarie (patrz ten link ).

Możesz użyć tej metody „elementu” w taki sam sposób jak driver.findElement. Na przykład:

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

Jeśli naprawdę chcesz poczekać kilka sekund na rozwiązanie problemu lub inną rzadką okazję, możesz utworzyć metodę pauzy podobną do tej, którą oferuje Selenium IDE:

    public void pause(Integer milliseconds){
    try {
        TimeUnit.MILLISECONDS.sleep(milliseconds);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
JohnP2
źródło
0

Odpowiedź : poczekaj kilka sekund, zanim widoczność elementu za pomocą Selenium WebDriver przejdzie przez poniższe metody.

implicitlyWait () : instancja WebDriver czeka na pełne załadowanie strony. Myślisz, że czekanie na pełne załadowanie strony trwa od 30 do 60 sekund.

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

ExplicitlyWait WebDriverWait () : instancja WebDriver czeka na pełne załadowanie strony.

WebDriverWait wait = new WebDriverWait(driver, 60);

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

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

Uwaga : użyj ExplicitlyWait WebDriverWait () do obsługi dowolnego określonego elementu WebElement.

sandeep shewale
źródło
0

Użyj działań -

Interfejs API skierowany do użytkownika do emulacji złożonych gestów użytkownika.

Zobacz metodę pauzy Actions # .

Dzmitry Sankouski
źródło
-1

Wolę, aby następujący kod czekał 2 sekundy.

for(int i=0; i<2 && driver.findElements(By.id("textbox")).size()==0 ; i++){
   Thread.sleep(1000);
}
Gangadhar
źródło
-1
Thread.sleep(1000);

jest gorzej: będąc statycznym czekaniem, skrypt testowy będzie wolniejszy.

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

to jest dynamiczne oczekiwanie

  • jest ważny do istnienia webdrivera lub ma zakres do życia sterownika
  • możemy również niejawnie czekać.

Wreszcie proponuję

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

z pewnymi predefiniowanymi warunkami:

isAlertPresent();
elementToBeSelected();
visibilityOfElementLocated();
visibilityOfAllElementLocatedBy();
frameToBeAvailableAndSwitchToIt();
  • To także dynamiczne czekanie
  • w tym przypadku czekanie potrwa tylko kilka sekund
  • musimy użyć jawnego czekania na określony element sieciowy, na którym chcemy użyć.
Shobhit
źródło
-4
Thread.Sleep(5000);

Pomogło mi to, ale należy zająć się wyjątkiem InterruptedException. Więc lepiej otocz go próbą złapania:

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

LUB

Dodaj deklarację rzutów:

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

Wolałbym drugi, ponieważ można go używać sleep()tyle razy, ile chce i unikać powtarzania tryi catchblokowania za każdym razem, sleep()gdy został użyty.

Prasad Hajare
źródło