Comment vérifier si un élément existe lors de l'utilisation d'objets de page avec webdriver.
Jusqu'ici je le fais de cette façon.
DefaultPage defaultPage = PageFactory.initElements(this.driver,
DefaultPage.class);
assertTrue(defaultPage.isUserCreateMenuLinkPresent());
Objet de page:
public class DefaultPage {
@FindBy(id = "link_i_user_create")
private WebElement userCreateMenuLink;
public boolean isUserCreateMenuLinkPresent() {
try {
this.userCreateMenuLink.getTagName();
return true;
} catch (NoSuchElementException e) {
return false;
}
}
}
Mais je ne peux pas croire que cet essai/attrape est la façon dont on devrait le faire. Alors, quel serait un meilleur moyen de vérifier si les éléments existent (avec l'utilisation d'objets de page)?
Webdriver est conçu pour lever une exception si un élément n'est pas trouvé. Il n'y a donc aucune méthode pour vérifier la présence d'un élément dans Webdriver.
Vérifiez ceci - http://groups.google.com/group/webdriver/browse_thread/thread/909a9b6cb568e341
Le problème est le motif lui-même. Il utilise l'annotation @FindBy (utilisée par PageFactory pour initialiser les champs devant être entourés par un proxy) qui remplace les éléments standard par leurs instances de proxy qui contiennent InvocationHandler.
Chaque fois que vous essayez d'accéder à un champ annoté avec @FindBy, le gestionnaire d'appels essaie de trouver l'élément à l'aide de ElementLocator par défaut. Le problème est que la méthode ElementLocator.findElement () lève une exception TimeoutException/NoSuchElementException si aucun élément n'est présenté. les DOM.
public WebElement findElement(SearchContext context) {
List<WebElement> allElements = findElements(context);
if (allElements == null || allElements.isEmpty())
throw new NoSuchElementException("Cannot locate an element using "
+ toString());
return allElements.get(0);
}
Par conséquent, chaque fois que vous devez vérifier si un élément est affiché ou non, vous devez rechercher une liste d'éléments et vérifier sa taille.
@FindBy(css = "div.custom")
private List<WebElement> elements
...
public isElementPresented(){
return elements != null && elements.size > 0
}
Une autre façon de résoudre ce problème consiste à créer votre propre implémentation de LocatingElementHandler et ElementLocator
Donc, si vous avez besoin de votre propre méthode isDisplayed () pour renvoyer false au lieu de Exception, vous devez remplacer la méthode findElement () dans ElementLocator par quelque chose comme ça:
...
List<WebElement> elements = searchContext.findElements(by)
if(elements != null && elements.size() > 0){
List<WebElement> visibleElements = []
elements.each {
if(it.displayed){
visibleElements.add(it)
}
}
if(visibleElements.size() > 0){
return visibleElements.get(0)
}
}
return null
...
Et ajoutez de nouvelles conditions à LocatingElementHandler.invoke ()
Quelque chose comme:
element = locator.findElement()
if(element == null){
if(method.name == "isDisplayed"){
return false
}
}
Je suis récemment tombé sur ce vieil article et je pense avoir trouvé une solution.
Je testais une page qui avait un bouton Add User
. Lorsque le bouton a été cliqué, divers champs de texte modifiables sont apparus (pour Prénom, Nom, Email, etc.) et un seul menu déroulant.
Quand un bouton Cancel
était cliqué, les champs disparaissaient et n'existaient plus. L'utilisation de WebDriverWait
avec ExpectedConditions.visibilityOf()
ne fonctionnerait pas car les éléments n'existaient plus dans DOM
.
J'ai trouvé que @FindAll
était une solution pour moi, bien que je dois avouer que mon test a été sensiblement lent à l'assertion de ma liste.
Pour votre code, quelque chose comme ceci:
public class DefaultPage {
@FindAll({@FindBy(id = "link_i_user_create")}) List<WebElement> userCreateMenuLink;
public boolean isUserCreateMenuLinkPresent() {
if (this.userCreateMenuLink.isEmpty()) fail("Link does not exist");}
Cependant, je peux utiliser quelque chose de similaire dans mes propres tests, et cela semble être un moyen sûr de contourner l'exception "aucun élément de ce type". Il s’agit essentiellement d’une adaptation d’objet de page à l’affirmation de: driver.findElements(By.locator).size() < 1
.
J'utilise ce modèle, cela fonctionne bien pour moi:
public void login()
{
if (!loginButton.isDisplayed())
{
throw new IllegalStateException("Login button is not displayed!");
} else
{
loginButton.click();
}
}
ou:
public boolean loginButtinIsDisplayed() {
try {
this.loginButton.getTagName();
return true;
} catch (NoSuchElementException e) {
e.printStackTrace();
return false;
}
}
Utilisation des liaisons C #:
using System.Collections.Generic;
using System.Linq;
public class DefaultPage
{
[FindsBy(How = How.Id, Using = "link_i_user_create")]
private IList<IWebElement> userCreateMenuLink;
public bool isUserCreateMenuLinkPresent()
{
return userCreateMenuLink.Any();
}
}
Vous dites à Selenium de saisir tous les éléments correspondant à cet identifiant et de les mettre dans une liste de IWebElement
. Vous appelez ensuite .Any()
sur la liste, qui a la valeur true si au moins un IWebElement
a été trouvé.
Arquillian a implémenté cette fonctionnalité dans Graphene extension.
Vérifiez ElementLocatorConditionFactory.isPresent()
function.
Ils font plus ou moins ce que vous avez écrit dans votre question (de ExpectedConditions.findElement
dans Selenium-support.jar ):
try {
return driver.findElement(by);
} catch (NoSuchElementException e) {
throw e;
} catch (WebDriverException e) {
// [...] some log
throw e;
}
@Ralph: Je le fais de la même manière: try/catch. Je n'ai jamais trouvé d'autre moyen… .. Vous pouvez échanger le bloc try/catch dans une super classe et le concevoir generic. En d'autres termes: vous pourriez écrire une méthode qui attend un objet de type WebElement . Cette méthode contient le bloc try/catch et renvoie true/false ...
J'ai donc écrit la méthode publique suivante dans le test framework 's super class et suis maintenant capable de l'utiliser dans chaque objet page :
public boolean isElementExisting(WebElement we) {
try {
we.isDisplayed();
return true;
} catch(NoSuchElementException e) {
LOGGER.severe("Element does not exist.");
return false;
}
}
Je ne sais pas pourquoi cela n'est pas implémenté dans WebDriver ...
Sinon, vous pouvez utiliser WebDriverWait .