Comment puis-je demander au Selenium-WebDriver d'attendre quelques secondes en Java?

100

Je travaille sur un Java Selenium-WebDriver. J'ai ajouté

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

et

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

car mes applications prennent quelques secondes pour charger l'interface utilisateur. J'ai donc mis 2 secondes d'attente implicite. mais je n'ai pas pu localiser la zone de texte de l'élément

Puis j'ajoute Thread.sleep(2000);

Maintenant ça marche bien. Quel est le meilleur moyen?

Prince
la source

Réponses:

123

Eh bien, il existe deux types d'attente: l'attente explicite et implicite. L'idée de l'attente explicite est

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

Le concept d'attente implicite est

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

Vous pouvez obtenir la différence dans les détails ici .

Dans de telles situations, je préférerais utiliser l'attente explicite ( fluentWaiten particulier):

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

fluentWaitLa fonction renvoie votre élément Web trouvé. De la documentation sur fluentWait: Une implémentation de l'interface Wait dont le délai d'expiration et l'intervalle d'interrogation peuvent être configurés à la volée. Chaque instance FluentWait définit la durée maximale d'attente d'une condition, ainsi que la fréquence à laquelle vérifier la condition. En outre, l'utilisateur peut configurer l'attente pour ignorer des types spécifiques d'exceptions pendant l'attente, comme NoSuchElementExceptions lors de la recherche d'un élément sur la page. Détails que vous pouvez obtenir ici

L'utilisation de fluentWaitdans votre cas soit la suivante:

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

Cette approche IMHO mieux car vous ne savez pas exactement combien de temps attendre et dans l'intervalle d'interrogation, vous pouvez définir une valeur de temps arbitraire à travers laquelle la présence de l'élément sera vérifiée. Cordialement.

eugene.polschikov
la source
3
Ce n'est import com.google.common.base.Function;pasimport java.util.function.Function;
je n'ai
haventchecked, en fait en utilisant intelij IDEA pour le développement, il aide avec les importations automatiques (lorsque vous travaillez dans un projet basé sur
maven
1
J'appelais simplement cela à d'autres qui se demandaient peut-être lequel Functionétait utilisé. Ce n'était pas évident pour moi au début. Merci d'avoir répondu.
haventchecked le
1
Ou vous pouvez simplement utiliser une expression lambda: driver -> driver.findElement(locator). Lorsque vous l'utilisez, vous n'aurez pas du tout besoin d'une instruction d'importation.
Thibstars
2
Maintenant c'est: .withTimeout(Duration.ofSeconds(30)).pollingEvery(Duration.ofSeconds(5))au lieu de: .withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
SuperRetro
16

Ce fil est un peu plus ancien, mais je pensais publier ce que je fais actuellement (travail en cours).

Bien que je rencontre toujours des situations où le système est soumis à une charge importante et lorsque je clique sur un bouton d'envoi (par exemple, login.jsp), les trois conditions (voir ci-dessous) reviennent truemais la page suivante (par exemple, home.jsp) n'a pas t a encore commencé le chargement.

Il s'agit d'une méthode d'attente générique qui prend une liste de 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;
}

J'ai défini diverses conditions attendues réutilisables (trois sont ci-dessous). Dans cet exemple, les trois conditions attendues incluent document.readyState = 'complete', pas de "wait_dialog" présent et pas de "spinners" (éléments indiquant que des données asynchrones sont demandées).

Seul le premier peut être appliqué de manière générique à toutes les pages 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;
    }
};

Selon la page, je peux en utiliser un ou tous:

waitForPageLoad(timeoutInSec,
            EXPECT_DOC_READY_STATE,
            EXPECT_NOT_WAITING,
            EXPECT_NO_SPINNERS
    );

Il existe également des ExpectedConditions prédéfinies dans la classe suivante: org.openqa.selenium.support.ui.ExpectedConditions

Jeff Vincent
la source
1
Très bonne réponse! Je n'ai jamais vu quelqu'un passer un élément ExpectedCondition via le constructeur de méthode. Quelle bonne idée. +1
djangofan
Jeff Vincent, pouvez-vous me dire si cela fonctionnera pour que la page attende 5 minutes si oui, veuillez suggérer quel paramètre de lieu doit envoyer?
khanam
C'est une excellente réponse. J'ai une fonction similaire comme celle-ci, cependant, je dois l'appeler dans chaque fonction pour que mon script attende le chargement de la page (spinner). Est-il possible que je puisse accrocher cette fonction waitForSpinner silencieusement dans ImplicitWait afin que je n'ai pas besoin de l'appeler à chaque fois dans ma fonction? Comme définir la fonction, accrochez-la au conducteur une fois et à la flèche.
Bhuvanesh Mani
13

Si vous utilisez webdriverJs (node.js),

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

Le code ci-dessus fait attendre le navigateur pendant 5 secondes après avoir cliqué sur le bouton.

Anup
la source
18
Pourquoi publieriez-vous ceci alors que la question concerne spécifiquement Java, pas le nœud / JavaScript? C'est aussi hors sujet que de savoir comment le faire en rubis.
Thor84no
1
recommandation d'utiliser le sommeil n'est certainement pas une bonne solution
Kiril S.
@ Thor84no Principalement depuis que certains d'entre nous recherchent la solution Web trouvent cette réponse
Kitanga Nday
10

L'utilisation Thread.sleep(2000);est une attente inconditionnelle. Si votre test se charge plus rapidement, vous devrez encore attendre. Donc, en principe, utiliser implicitlyWaitest la meilleure solution.

Cependant, je ne vois pas pourquoi implicitlyWaitcela ne fonctionne pas dans votre cas. Avez-vous mesuré si le findElementprend en fait deux secondes avant de lancer une exception. Si tel est le cas, pouvez-vous essayer d'utiliser l'attente conditionnelle de WebDriver comme décrit dans cette réponse?

Valentin
la source
3

J'aime utiliser des conditions personnalisées. Voici du code en 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)

Chaque fois que vous avez besoin d'attendre, vous pouvez le faire explicitement en vérifiant l'existence d'un certain élément (un tel élément peut varier d'une page à l'autre). find_elements_by_idrenvoie la liste - vide ou non, il vous suffit de vérifier.

simno
la source
2

le clic semble bloquer? - voici une autre façon d'attendre si vous utilisez WebDriverJS:

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

Le code ci-dessus attend après avoir cliqué sur le bouton pour que la page suivante se charge, puis saisit la source de la page suivante.

Dion
la source
1
Qu'est-ce qui fait que le code attend le chargement de la page suivante? Est-ce simplement que le premier appel dans le rappel est getPageSource?
isochrone
1

Attendre implicitement et Thread.sleep Les deux sont utilisés pour la synchronisation uniquement ... mais la différence est que nous pouvons utiliser implicitement attendre tout le programme, mais Thread.sleep ne fonctionnera que pour ce code unique ... Ici, ma suggestion est d'utiliser implicitement attendre une fois dans le programme quand chaque fois que votre page Web sera actualisée, cela signifie que vous utilisez Thread.sleep à ce moment-là..il sera beaucoup mieux :)

Voici mon 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
la source
0

Parfois, l'attente implicite semble être remplacée et le temps d'attente est raccourci. [@ eugene.polschikov] avait une bonne documentation sur les pourquoi. J'ai trouvé dans mes tests et mon codage avec Selenium 2 que les attentes implicites sont bonnes, mais parfois vous devez attendre explicitement.

Il vaut mieux éviter d'appeler directement un thread pour dormir, mais parfois il n'y a pas de bon moyen de le contourner. Cependant, il existe d'autres options d'attente fournies par Selenium qui aident. waitForPageToLoad et waitForFrameToLoad se sont avérés particulièrement utiles.

BadgerAndK
la source
0

Attente implicite: pendant l'attente implicite, si le pilote Web ne peut pas le trouver immédiatement en raison de sa disponibilité, le WebDriver attendra l'heure mentionnée et il n'essaiera pas de retrouver l'élément pendant la période spécifiée. Une fois le temps spécifié écoulé, il essaiera de rechercher à nouveau l'élément la dernière fois avant de lancer l'exception. Le paramètre par défaut est zéro. Une fois que nous avons défini une heure, le pilote Web attend la période de l'instance d'objet WebDriver.

Attente explicite: il peut y avoir des cas où un élément particulier prend plus d'une minute à charger. Dans ce cas, vous n'aimez certainement pas définir un temps énorme d'attente implicite, car si vous le faites, votre navigateur attendra la même heure pour chaque élément. Pour éviter cette situation, vous pouvez simplement mettre une heure distincte sur l'élément requis uniquement. En suivant cela, le temps d'attente implicite de votre navigateur serait court pour chaque élément et il serait important pour un élément spécifique.

Abhishek Singh
la source
0

Parfois, l'attente implicite échoue, disant qu'un élément existe mais ce n'est vraiment pas le cas.

La solution consiste à éviter d'utiliser driver.findElement et à le remplacer par une méthode personnalisée qui utilise implicitement une attente explicite. Par exemple:

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

Il existe des raisons supplémentaires pour éviter les attentes implicites autres que les échecs sporadiques et occasionnels (voir ce lien ).

Vous pouvez utiliser cette méthode "element" de la même manière que driver.findElement. Par exemple:

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

Si vous voulez vraiment attendre quelques secondes pour le dépannage ou une autre occasion rare, vous pouvez créer une méthode de pause similaire à ce que propose sélénium IDE:

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

Réponse : attendez quelques secondes avant que la visibilité des éléments à l'aide de Selenium WebDriver ne passe par les méthodes ci-dessous.

implicitlyWait () : l'instance WebDriver attend le chargement complet de la page. Vous pensez utiliser 30 à 60 secondes pour attendre le chargement complet de la page.

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

ExplicitlyWait WebDriverWait () : l'instance WebDriver attend le chargement complet de la page.

WebDriverWait wait = new WebDriverWait(driver, 60);

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

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

Remarque : veuillez utiliser ExplicitlyWait WebDriverWait () pour gérer un WebElement particulier.

Sandeep Shewale
la source
0

Utiliser des actions -

L'API destinée à l'utilisateur pour émuler les gestes complexes des utilisateurs.

Voir la méthode Actions # pause .

Dzmitry Sankouski
la source
-1

Je préfère que le code suivant attende 2 secondes.

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

est le pire: étant une attente statique, cela ralentira le script de test.

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

c'est une attente dynamique

  • il est valide jusqu'à l'existence du pilote Web ou a une portée jusqu'à la durée de vie du pilote
  • nous pouvons également attendre implicitement.

Enfin, ce que je suggère est

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

avec quelques conditions prédéfinies:

isAlertPresent();
elementToBeSelected();
visibilityOfElementLocated();
visibilityOfAllElementLocatedBy();
frameToBeAvailableAndSwitchToIt();
  • C'est aussi une attente dynamique
  • en cela l'attente ne sera que de quelques secondes
  • nous devons utiliser l'attente explicite pour un élément Web particulier sur lequel nous voulons utiliser.
Shobhit
la source
-4
Thread.Sleep(5000);

Cela m'a aidé, mais l'exception InterruptedException doit être prise en charge. Alors mieux vaut l'entourer d'essayer et d'attraper:

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

OU

Ajouter une déclaration throws:

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

Je préférerais le second car on peut alors utiliser sleep()autant de fois qu'il le souhaite et éviter la répétition tryet le catchblocage à chaque fois où sleep()a été utilisé.

Prasad Hajare
la source