Je suis nouveau sur Selenium WebDriver et j'essaie de comprendre la bonne façon d'attendre que des éléments soient présents.
Je teste une page avec un tas de questions qui ont des réponses par bouton radio. Lorsque vous sélectionnez des réponses, Javascript peut activer/désactiver certaines des questions de la page.
Le problème semble être que Selenium "clique trop vite" et n'attend pas la fin du Javascript. J'ai essayé de résoudre ce problème de deux manières - des attentes explicites ont résolu le problème. Plus précisément, cela fonctionne et résout mon problème:
private static WebElement findElement(final WebDriver driver, final By locator, final int timeoutSeconds) {
FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(timeoutSeconds, TimeUnit.SECONDS)
.pollingEvery(500, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class);
return wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver webDriver) {
return driver.findElement(locator);
}
});
}
Cependant, je préfère utiliser une attente implicite au lieu de cela. J'ai mon pilote Web configuré comme ceci:
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
Cela ne résout pas le problème et j'obtiens une NoSuchElementException. De plus, je ne remarque pas de pause de 10 secondes - cela entraîne une erreur immédiate. J'ai vérifié cette ligne dans le code est frappé avec un débogueur. Qu'est-ce que je fais mal? Pourquoi n'attend-il pas implicitement que l'élément apparaisse, mais FluentWait le fait?
Remarque: comme je l'ai déjà mentionné, je souhaite simplement savoir pourquoi l'attente implicite ne résout pas mon problème. Merci.
N'oubliez pas qu'il existe une différence entre plusieurs scénarios:
Je suppose que si certaines pages sont affichées en javascript, les éléments sont déjà présents dans le DOM du navigateur, mais ne sont pas visibles. L'attente implicite n'attend qu'un élément d'apparaître dans le DOM; il est donc renvoyé immédiatement. Toutefois, lorsque vous essayez d'interagir avec l'élément, vous obtenez une exception NoSuchElementException. Vous pouvez tester cette hypothèse en écrivant une méthode d'assistance expliquant l'attente qu'un élément soit visible ou cliquable.
Quelques exemples (en Java):
public WebElement getWhenVisible(By locator, int timeout) {
WebElement element = null;
WebDriverWait wait = new WebDriverWait(driver, timeout);
element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
return element;
}
public void clickWhenReady(By locator, int timeout) {
WebDriverWait wait = new WebDriverWait(driver, timeout);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(locator));
element.click();
}
l'idée de base est la suivante:
Attente explicite
WebDriverWait.until(condition-that-finds-the-element);
Attente implicite
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
En d'autres termes, explicit est associé à une condition à maintenir, alors qu'implicite à un certain temps d'attendre quelque chose . voir ce lien
Pour que le travail soit fluideAttendez correctement, essayez ceci:
public WebElement fluentWait(final By locator){
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofMillis(100))
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(
new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
}
);
return foo;
};
J'espère que cela t'aides)
Mot d'avertissement pour une erreur commune:
Une fois que vous avez défini l'attente implicite, vous ne pouvez pas utiliser l'attente explicite ou fluide jusqu'à ce que vous réinitialisiez à nouveau l'attente implicite. Cela signifie que ExpectedConditions
, qui contient des appels driver.findElement
, ne fonctionnera pas comme prévu avec une attente implicite! Vous rencontrerez souvent des cas dans lesquels vous souhaitez vérifier instantanément un élément ou sa non-existence - mais vous ne pouvez pas le faire non plus.
Après environ 2 ans d'expérience et des problèmes avec cela, je recommande fortement de ne pas utiliser l'attente implicite.
Une version kotlin de la https://stackoverflow.com/users/503060/hedley answer:
clickWhenReady("#suggest",10,driver)
via
fun clickWhenReady(selector: String,timeout: Long, webdriver: WebDriver?) {
val wait = WebDriverWait(webdriver, timeout);
val element = wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector(selector)));
element.click();
}
J'ai une autre solution pour résoudre ce problème (uniquement pour IE, je n'essaye jamais avec un autre navigateur):
1) après avoir créé l’instance de pilote Selenium, vous pouvez obtenir son instance COM, par exemple
Add-Type -Path .\SePSX.NET35\WebDriver.dll
$ieDriver = New-Object "OpenQA.Selenium.IE.InternetExplorerDriver"
$ieShell = $null
$Shell_apps = (New-Object -ComObject Shell.Application).Windows()
foreach($app in $Shell_apps)
{
if ($app.LocationURL -eq $ieDriver.URL)
{
$ieShell = $app
break
}
}
if ($ieShell -eq $null)
{
throw "Can't get WebDriver IE Instance"
}
2) après chaque appel GotoURL ou action de clic, vérifiez le statut $ ieShell.Busy, il attendra que la page soit chargée.
$ieDriver.Navigate().GotoUrl("www.google.com")
while ($ieShell.Busy -eq $true) {sleep 1}
then call Selenium driver to get element id and do the further action
$ieDriver.FindElementById ...
utilisez cette méthode, vous n'avez pas besoin de définir le chargement de page et le délai d'expiration de findElement pour Selenium
import org.openqa.Selenium.NoSuchElementException;
import org.openqa.Selenium.WebDriver;
import org.openqa.Selenium.WebElement;
import org.openqa.Selenium.support.ui.FluentWait;
import org.openqa.Selenium.support.ui.Wait;
FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver);
wait.pollingEvery(250, TimeUnit.MILLISECONDS);
wait.withTimeout(20, TimeUnit.SECONDS);
wait.ignoring(NoSuchElementException.class);
Predicate<WebDriver> predicate = new Predicate <WebDriver>()
{
public boolean apply(WebDriver arg0) {
WebElement element = arg0.findElement(By.id("colorVar"));
String color = element.getAttribute("color");
System.out.println("The color if the button is " + color);
if(color.equals("blue"))
{
return true;
}
return false;
}
};
wait.until(predicate);
J'ai écrit une petite méthode en C # en utilisant la classe WebDriverWait. Ça marche bien pour moi.
public static void WaitForAjaxElement(IWebDriver driver, By byElement, double timeoutSeconds)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds));
wait.Until(x => x.FindElement(byElement));
}
En utilisant:
WaitForAjaxElement(driver, By.ClassName("ui-menu-item"), 10);
J'espère que ça aide.
De Seleniumhq.com:
Une attente implicite consiste à demander à WebDriver d'interroger le DOM pendant un certain temps lors de la recherche d'un ou plusieurs éléments s'ils ne sont pas immédiatement disponibles. Le paramètre par défaut est 0. Une fois défini, l'attente implicite est définie pour la durée de vie de l'instance d'objet WebDriver.
Si vous publiez votre code de test sur ce que vous voulez réellement faire, je peux vous fournir plus d'informations.