Come posso chiedere al Selenium-WebDriver di attendere qualche secondo in Java?


100

Sto lavorando a un Java Selenium-WebDriver. Ho aggiunto

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

e

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

perché le mie applicazioni impiegano pochi secondi per caricare l'interfaccia utente. Quindi ho impostato 2 secondi di attesa implicita. ma non sono riuscito a individuare la casella di testo dell'elemento

Poi aggiungo Thread.sleep(2000);

Adesso funziona bene. Qual è il modo migliore?


Risposte:


123

Bene, ci sono due tipi di attesa: attesa esplicita e implicita. L'idea dell'attesa esplicita è

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

Il concetto di attesa implicita è

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

Puoi ottenere la differenza nei dettagli qui .

In tali situazioni preferirei utilizzare l'attesa esplicita ( fluentWaitin particolare):

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

fluentWaitrestituisce l'elemento web trovato. Dalla documentazione in poi fluentWait: un'implementazione dell'interfaccia Wait che può avere il suo timeout e l'intervallo di polling configurati al volo. Ogni istanza di FluentWait definisce la quantità massima di tempo di attesa per una condizione, nonché la frequenza con cui controllare la condizione. Inoltre, l'utente può configurare l'attesa per ignorare tipi specifici di eccezioni durante l'attesa, come NoSuchElementExceptions durante la ricerca di un elemento nella pagina. Dettagli che puoi ottenere qui

L'utilizzo di fluentWaitnel tuo caso è il seguente:

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

Questo approccio IMHO è migliore in quanto non sai esattamente quanto tempo aspettare e nell'intervallo di polling puoi impostare un valore temporale arbitrario attraverso il quale verrà verificata la presenza dell'elemento. Saluti.


3
Cioè import com.google.common.base.Function;, nonimport java.util.function.Function;
verificato il

haventchecked, in realtà utilizzando intelij IDEA per lo sviluppo, aiuta con le importazioni automatiche (quando si lavora in un progetto basato su
Maven

1
Lo stavo solo chiamando per altri che potrebbero chiedersi quale Functionfosse stato utilizzato. All'inizio non era ovvio per me. Grazie per la risposta.
haventchecked il

1
Oppure si potrebbe utilizzare un'espressione lambda: driver -> driver.findElement(locator). Quando lo usi non avrai bisogno di una dichiarazione di importazione.
Thibstars

2
Ora è: .withTimeout(Duration.ofSeconds(30)).pollingEvery(Duration.ofSeconds(5))al posto di: .withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
SuperRetro

16

Questo thread è un po 'più vecchio, ma ho pensato di pubblicare quello che faccio attualmente (lavori in corso).

Anche se sto ancora riscontrando situazioni in cui il sistema è sotto carico pesante e quando faccio clic su un pulsante di invio (ad es. Login.jsp), tutte e tre le condizioni (vedi sotto) ritornano truema la pagina successiva (ad es. Home.jsp) non ha ' t iniziato a caricare ancora.

Questo è un metodo di attesa generico che accetta un elenco di 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;
}

Ho definito varie ExpectedConditions riutilizzabili (tre sono sotto). In questo esempio, le tre condizioni previste includono document.readyState = 'complete', nessun "wait_dialog" presente e nessuno 'spinner' (elementi che indicano che vengono richiesti dati asincroni).

Solo il primo può essere applicato genericamente a tutte le pagine web.

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

A seconda della pagina, posso usarne uno o tutti:

waitForPageLoad(timeoutInSec,
            EXPECT_DOC_READY_STATE,
            EXPECT_NOT_WAITING,
            EXPECT_NO_SPINNERS
    );

Esistono anche ExpectedConditions predefinite nella classe seguente: org.openqa.selenium.support.ui.ExpectedConditions


1
Bella risposta! Non ho mai visto qualcuno passare un elemento ExpectedCondition tramite il costruttore del metodo. Che bella idea. +1
Djangofan

Jeff Vincent, puoi dirmi che funzionerà per la pagina in attesa di 5 minuti se sì, allora suggerisci quale parametro del luogo deve essere inviato?
khanam

Questa è un'ottima risposta. Ho una funzione simile come questa, tuttavia, devo chiamarla in ogni funzione in modo che il mio script attenda fino al caricamento della pagina (spinner). C'è un modo per agganciare silenziosamente questa funzione waitForSpinner a ImplicitWait in modo da non doverla chiamare ogni volta nella mia funzione? Come definire la funzione, agganciarlo al driver una volta e boom.
Bhuvanesh Mani

13

Se si utilizza webdriverJs (node.js),

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

Il codice sopra fa attendere il browser per 5 secondi dopo aver fatto clic sul pulsante.


18
Perché pubblicheresti questo messaggio quando la domanda riguarda specificamente Java, non node / JavaScript? È fuori tema come rispondere a come farlo in rubino.
Thor84no

1
la raccomandazione di usare il sonno non è sicuramente una buona soluzione
Kiril S.

@ Thor84no Principalmente poiché alcuni di noi che cercano la soluzione web trovano questa risposta
Kitanga Nday,

10

L'utilizzo Thread.sleep(2000);è un'attesa incondizionata. Se il tuo test viene caricato più velocemente, dovrai comunque aspettare. Quindi in linea di principio l'utilizzo implicitlyWaitè la soluzione migliore.

Tuttavia, non vedo perché implicitlyWaitnon funziona nel tuo caso. Hai misurato se findElementimpiega effettivamente due secondi prima di generare un'eccezione. In tal caso, puoi provare a utilizzare l'attesa condizionale di WebDriver come descritto in questa risposta?


3

Mi piace usare condizioni personalizzate. Ecco un po 'di codice 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)

Ogni volta che hai bisogno di aspettare, puoi farlo esplicitamente controllando l'esistenza di un certo elemento (tale elemento può variare da pagina a pagina). find_elements_by_idrestituisce la lista - vuota o no, devi solo controllare.


2

il clic sembra bloccare? - ecco un altro modo per aspettare se stai usando WebDriverJS:

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

Il codice sopra attende dopo aver fatto clic sul pulsante per il caricamento della pagina successiva e quindi acquisisce la fonte della pagina successiva.


1
Cosa fa aspettare il codice per il caricamento della pagina successiva? È solo che la prima chiamata nella richiamata è getPageSource?
Isochronous

1

Implicitly wait e Thread.sleep Entrambi sono usati solo per la sincronizzazione ... ma la differenza è che possiamo usare Implicitly wait for intero programma ma Thread.sleep funzionerà solo per quel singolo codice .. Qui il mio suggerimento è di usare Implicitly wait una volta nel programma quando ogni volta che la tua pagina web verrà aggiornata significa che usa Thread.sleep in quel momento..sarà molto meglio :)

Ecco il mio codice:

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

A volte l'attesa implicita sembra essere sovrascritta e il tempo di attesa viene ridotto. [@ eugene.polschikov] aveva una buona documentazione sui perché. Ho trovato nei miei test e codifica con Selenium 2 che le attese implicite sono buone ma a volte devi aspettare esplicitamente.

È meglio evitare di chiamare direttamente un thread per dormire, ma a volte non c'è un buon modo per aggirarlo. Tuttavia, ci sono altre opzioni di attesa fornite da Selenium che aiutano. waitForPageToLoad e waitForFrameToLoad si sono dimostrati particolarmente utili.


0

Attesa implicita: durante l'attesa implicita se il driver Web non è in grado di trovarlo immediatamente a causa della sua disponibilità, WebDriver attenderà per il tempo indicato e non tenterà di trovare nuovamente l'elemento durante il periodo di tempo specificato. Una volta trascorso il tempo specificato, proverà a cercare l'elemento ancora una volta l'ultima volta prima di lanciare un'eccezione. L'impostazione predefinita è zero. Una volta impostato un tempo, il driver Web attende il periodo dell'istanza dell'oggetto WebDriver.

Attesa esplicita: può esserci un'istanza in cui un particolare elemento impiega più di un minuto per caricarsi. In tal caso sicuramente non ti piace impostare un tempo enorme per l'attesa implicita, come se lo fai il tuo browser aspetterà lo stesso tempo per ogni elemento. Per evitare questa situazione puoi semplicemente mettere un tempo separato solo sull'elemento richiesto. Seguendo questo, il tempo di attesa implicito del browser sarebbe breve per ogni elemento e grande per l'elemento specifico.


0

A volte l'attesa implicita fallisce, dicendo che un elemento esiste ma in realtà non lo è.

La soluzione è evitare di utilizzare driver.findElement e sostituirlo con un metodo personalizzato che utilizza implicitamente un'attesa esplicita. Per esempio:

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

Esistono altri motivi per evitare l'attesa implicita oltre a sporadici e occasionali fallimenti (vedere questo collegamento ).

È possibile utilizzare questo metodo "elemento" allo stesso modo di driver.findElement. Per esempio:

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

Se vuoi davvero aspettare solo pochi secondi per la risoluzione dei problemi o qualche altra rara occasione, puoi creare un metodo di pausa simile a quello che offre l'IDE selenio:

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

0

Risposta : attendere alcuni secondi prima che la visibilità dell'elemento utilizzando Selenium WebDriver passi attraverso i metodi seguenti.

implicitlyWait () : l'istanza WebDriver attende il caricamento della pagina completa. Usa dai 30 ai 60 secondi per attendere il caricamento della pagina intera.

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

ExplicitlyWait WebDriverWait () : l'istanza WebDriver attende il caricamento della pagina completa.

WebDriverWait wait = new WebDriverWait(driver, 60);

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

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

Nota : utilizzare ExplicitlyWait WebDriverWait () per gestire un particolare WebElement.



-1

Preferisco che il codice seguente attenda 2 secondi.

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

-1
Thread.sleep(1000);

è il peggio: essendo un'attesa statica, renderà lo script di test più lento.

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

questa è un'attesa dinamica

  • è valido fino all'esistenza del webdriver o ha una portata fino alla vita del driver
  • possiamo anche aspettare implicitamente.

Infine, quello che suggerisco è

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

con alcune condizioni predefinite:

isAlertPresent();
elementToBeSelected();
visibilityOfElementLocated();
visibilityOfAllElementLocatedBy();
frameToBeAvailableAndSwitchToIt();
  • È anche attesa dinamica
  • in questo l'attesa sarà solo in pochi secondi
  • dobbiamo usare l'attesa esplicita per un particolare elemento web su cui vogliamo usare.

-4
Thread.Sleep(5000);

Questo mi ha aiutato, ma è necessario occuparsi dell'eccezione InterruptException. Quindi è meglio circondarlo con try and catch:

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

O

Aggiungi dichiarazione di lanci:

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

Preferirei il secondo poiché si può quindi utilizzare sleep()tutte le volte che vuole ed evitare la ripetizione trye il catchblocco ogni volta che sleep()è stato utilizzato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.