Wie kann ich den Selenium-WebDriver bitten, in Java einige Sekunden zu warten?

100

Ich arbeite an einem Java Selenium-WebDriver. Ich fügte hinzu

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

und

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

weil das Laden der Benutzeroberfläche für meine Anwendungen einige Sekunden dauert. Also habe ich 2 Sekunden implizites Warten eingestellt. Ich konnte jedoch das Elementtextfeld nicht finden

Dann füge ich hinzu Thread.sleep(2000);

Jetzt funktioniert es gut. Welcher ist ein besserer Weg?

Prinz
quelle

Antworten:

123

Nun, es gibt zwei Arten von Wartezeiten: explizite und implizite Wartezeiten. Die Idee des expliziten Wartens ist

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

Das Konzept des impliziten Wartens ist

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

Details können Sie hier erfahren .

In solchen Situationen würde ich es vorziehen, explizit zu warten ( fluentWaitinsbesondere):

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

fluentWaitFunktion gibt Ihr gefundenes Webelement zurück. Aus der Dokumentation zu fluentWait: Eine Implementierung der Wait-Schnittstelle, deren Zeitlimit und Abfrageintervall möglicherweise im laufenden Betrieb konfiguriert sind. Jede FluentWait-Instanz definiert die maximale Wartezeit auf eine Bedingung sowie die Häufigkeit, mit der die Bedingung überprüft wird. Darüber hinaus kann der Benutzer das Warten so konfigurieren, dass bestimmte Arten von Ausnahmen während des Wartens ignoriert werden, z. B. NoSuchElementExceptions, wenn nach einem Element auf der Seite gesucht wird. Details erhalten Sie hier

Verwendung fluentWaitin Ihrem Fall wie folgt sein:

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

Dieser Ansatz ist meiner Meinung nach besser, da Sie nicht genau wissen, wie viel Zeit Sie warten müssen, und Sie im Abfrageintervall einen beliebigen Zeitwert festlegen können, durch den das Vorhandensein von Elementen überprüft wird. Grüße.

eugene.polschikov
quelle
3
Das ist import com.google.common.base.Function;nichtimport java.util.function.Function;
habe am
haventchecked, tatsächlich verwendet Intelij IDEA für die Entwicklung, hilft es bei Auto-Importen (wenn Sie in Maven-basierten Projekt arbeiten)
Eugene.polschikov
1
Ich habe das nur für andere gerufen, die sich vielleicht fragen, welche Functionverwendet werden. Das war mir zunächst nicht klar. Danke für die Antwort.
Habe am
1
Oder Sie könnten einfach einen Lambda-Ausdruck verwenden : driver -> driver.findElement(locator). Wenn Sie dies verwenden, benötigen Sie überhaupt keine Importanweisung.
Thibstars
2
Jetzt ist es: .withTimeout(Duration.ofSeconds(30)).pollingEvery(Duration.ofSeconds(5))anstelle von: .withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
SuperRetro
16

Dieser Thread ist etwas älter, dachte aber, ich würde posten, was ich gerade mache (in Arbeit).

Obwohl ich immer noch auf Situationen stoße, in denen das System stark ausgelastet ist und wenn ich auf eine Senden-Schaltfläche (z. B. login.jsp) klicke, kehren alle drei Bedingungen (siehe unten) zurück, trueaber die nächste Seite (z. B. home.jsp) ist nicht vorhanden. Ich habe noch nicht mit dem Laden begonnen.

Dies ist eine generische Wartemethode, die eine Liste von ExpectedConditions erstellt.

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

Ich habe verschiedene wiederverwendbare ExpectedConditions definiert (drei davon sind unten aufgeführt). In diesem Beispiel umfassen die drei erwarteten Bedingungen document.readyState = 'complete', kein "wait_dialog" und keine 'spinners' (Elemente, die angeben, dass asynchrone Daten angefordert werden).

Nur die erste kann generisch auf alle Webseiten angewendet werden.

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

Abhängig von der Seite kann ich eine oder alle verwenden:

waitForPageLoad(timeoutInSec,
            EXPECT_DOC_READY_STATE,
            EXPECT_NOT_WAITING,
            EXPECT_NO_SPINNERS
    );

Es gibt auch vordefinierte ExpectedConditions in der folgenden Klasse: org.openqa.selenium.support.ui.ExpectedConditions

Jeff Vincent
quelle
1
Gute Antwort! Ich habe noch nie jemanden gesehen, der ein ExpectedCondition-Element über den Methodenkonstruktor übergeben hat. Was für eine großartige Idee. +1
Djangofan
Jeff Vincent, können Sie mir bitte sagen, ob diese Seite 5 Minuten warten soll, wenn ja, dann schlagen Sie bitte vor, welcher Ortsparameter gesendet werden muss.
Khanam
Dies ist eine großartige Antwort. Ich habe eine ähnliche Funktion wie diese, muss sie jedoch in jeder Funktion aufrufen, damit mein Skript bis zum Laden der Seite (Spinner) wartet. Gibt es eine Möglichkeit, diese waitForSpinner-Funktion stillschweigend in ImplicitWait einzubinden, damit ich sie nicht jedes Mal in meiner Funktion aufrufen muss? Wie die Funktion definieren, haken Sie sie einmal an den Fahrer und boomen.
Bhuvanesh Mani
13

Wenn Sie webdriverJs (node.js) verwenden,

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

Der obige Code lässt den Browser nach dem Klicken auf die Schaltfläche 5 Sekunden warten.

Anup
quelle
18
Warum sollten Sie dies posten, wenn sich die Frage speziell auf Java bezieht, nicht auf Node / JavaScript? Es ist so unangebracht wie zu antworten, wie man es in Rubin macht.
Thor84no
1
Empfehlung, Schlaf zu verwenden, ist definitiv keine gute Lösung
Kiril S.
@ Thor84no Hauptsächlich, da einige von uns, die nach der
Weblösung
10

Die Verwendung Thread.sleep(2000);ist ein bedingungsloses Warten. Wenn Ihr Test schneller geladen wird, müssen Sie noch warten. Also im Prinzip mitimplicitlyWait ist also die bessere Lösung.

Ich verstehe jedoch nicht, warum implicitlyWaitdies in Ihrem Fall nicht funktioniert. Haben Sie gemessen, ob das findElementtatsächlich zwei Sekunden dauert, bevor eine Ausnahme ausgelöst wird? Wenn ja, können Sie versuchen, die bedingte Wartezeit von WebDriver wie in dieser Antwort beschrieben zu verwenden?

Valentin
quelle
3

Ich verwende gerne benutzerdefinierte Bedingungen. Hier ist ein Code in 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)

Wann immer Sie warten müssen, können Sie dies explizit tun, indem Sie die Existenz eines bestimmten Elements überprüfen (dieses Element kann von Seite zu Seite variieren). find_elements_by_idRückgabeliste - leer oder nicht, Sie müssen nur überprüfen.

simno
quelle
2

Klick scheint zu blockieren? - Hier ist eine andere Möglichkeit zu warten, wenn Sie WebDriverJS verwenden:

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

Der obige Code wartet, nachdem auf die Schaltfläche geklickt wurde, bis die nächste Seite geladen ist, und greift dann auf die Quelle der nächsten Seite zu.

Dion
quelle
1
Wodurch wartet der Code auf das Laden der nächsten Seite? Ist es nur so, dass der erste Aufruf im Rückruf getPageSource ist?
Isochronous
1

Implizit warten und Thread.sleep Beide werden nur für die Synchronisation verwendet. Der Unterschied besteht jedoch darin, dass wir implizit auf das gesamte Programm warten können, Thread.sleep jedoch nur für diesen einzelnen Code funktioniert. Hier ist mein Vorschlag, implizit einmal im Programm warten Wenn Ihre Webseite jedes Mal aktualisiert wird, verwenden Sie Thread.sleep zu diesem Zeitpunkt. Es wird viel besser :)

Hier ist mein Code:

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
quelle
0

Manchmal scheint das implizite Warten überschrieben zu werden und die Wartezeit wird verkürzt. [@ eugene.polschikov] hatte eine gute Dokumentation über das Warum. Ich habe in meinen Tests und Codierungen mit Selenium 2 festgestellt, dass implizite Wartezeiten gut sind, aber gelegentlich müssen Sie explizit warten.

Es ist besser zu vermeiden, direkt einen Thread zum Schlafen zu rufen, aber manchmal gibt es keinen guten Weg, um ihn zu umgehen. Es gibt jedoch andere von Selen bereitgestellte Warteoptionen, die helfen. waitForPageToLoad und waitForFrameToLoad haben sich als besonders nützlich erwiesen.

BadgerAndK
quelle
0

Implizites Warten: Während des impliziten Wartens wartet der WebDriver auf die angegebene Zeit und versucht nicht, das Element während des angegebenen Zeitraums erneut zu finden, wenn der Webtreiber es aufgrund seiner Verfügbarkeit nicht sofort finden kann. Sobald die angegebene Zeit abgelaufen ist, wird versucht, das Element das letzte Mal erneut zu durchsuchen, bevor eine Ausnahme ausgelöst wird. Die Standardeinstellung ist Null. Sobald wir eine Zeit festgelegt haben, wartet der Web-Treiber auf den Zeitraum der WebDriver-Objektinstanz.

Explizites Warten: Es kann vorkommen, dass das Laden eines bestimmten Elements länger als eine Minute dauert. In diesem Fall möchten Sie definitiv keine große Zeit für implizites Warten festlegen. Wenn Sie dies tun, wartet Ihr Browser für jedes Element zur gleichen Zeit. Um diese Situation zu vermeiden, können Sie einfach nur für das erforderliche Element eine separate Zeit festlegen. Wenn Sie dies befolgen, ist die implizite Wartezeit Ihres Browsers für jedes Element kurz und für ein bestimmtes Element groß.

Abhishek Singh
quelle
0

Manchmal schlägt implizites Warten fehl und sagt, dass ein Element existiert, aber es existiert wirklich nicht.

Die Lösung besteht darin, die Verwendung von driver.findElement zu vermeiden und es durch eine benutzerdefinierte Methode zu ersetzen, die implizit eine explizite Wartezeit verwendet. Beispielsweise:

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

Es gibt weitere Gründe, um implizites Warten zu vermeiden, außer sporadischen, gelegentlichen Fehlern (siehe diesen Link ).

Sie können diese "Element" -Methode genauso wie driver.findElement verwenden. Beispielsweise:

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

Wenn Sie wirklich nur ein paar Sekunden auf die Fehlerbehebung oder eine andere seltene Gelegenheit warten möchten, können Sie eine Pausenmethode erstellen, die der von Selenium IDE ähnelt:

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

Antwort : Warten Sie einige Sekunden, bevor die Sichtbarkeit der Elemente mit Selenium WebDriver die folgenden Methoden durchläuft.

implicitlyWait () : Die WebDriver-Instanz wartet bis zum vollständigen Laden der Seite. Sie müssen 30 bis 60 Sekunden verwenden, um das Laden der gesamten Seite abzuwarten.

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

ExplicitlyWait WebDriverWait () : Die WebDriver-Instanz wartet bis zum vollständigen Laden der Seite.

WebDriverWait wait = new WebDriverWait(driver, 60);

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

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

Hinweis : Verwenden Sie ExplicitlyWait WebDriverWait (), um ein bestimmtes WebElement zu verarbeiten.

Sandeep Shewale
quelle
0

Aktionen verwenden -

Die benutzerbezogene API zum Emulieren komplexer Benutzergesten.

Siehe Aktion # Pause- Methode.

Dzmitry Sankouski
quelle
-1

Ich bevorzuge den folgenden Code, um 2 Sekunden zu warten.

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

ist das Schlimmste: Da es sich um ein statisches Warten handelt, wird das Testskript langsamer.

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

Dies ist ein dynamisches Warten

  • Es ist gültig bis zur Existenz eines Web-Treibers oder hat einen Gültigkeitsbereich bis zur Lebensdauer des Treibers
  • wir können auch implizit warten.

Schließlich schlage ich vor

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

mit einigen vordefinierten Bedingungen:

isAlertPresent();
elementToBeSelected();
visibilityOfElementLocated();
visibilityOfAllElementLocatedBy();
frameToBeAvailableAndSwitchToIt();
  • Es ist auch dynamisches Warten
  • In diesem Fall dauert das Warten nur Sekunden
  • Wir müssen explizit auf ein bestimmtes Webelement warten, für das wir es verwenden möchten.
Shobhit
quelle
-4
Thread.Sleep(5000);

Dies hat mir geholfen, aber die InterruptedException-Ausnahme muss behoben werden. Umgib es also besser mit try and catch:

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

ODER

Wurferklärung hinzufügen:

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

Ich würde den zweiten vorziehen, da man ihn dann sleep()so oft verwenden kann, wie er möchte, und die Wiederholung tryund catchBlockierung jedes Mal vermeiden kann, wenn sleep()er verwendet wurde.

Prasad Hajare
quelle