WebDriver:如何检查页面对象Web元素是否存在?

发布于 2024-11-17 15:38:43 字数 702 浏览 3 评论 0原文

将页面对象与 webdriver 结合使用时如何检查元素是否存在。

到目前为止我都是这样做的。

DefaultPage defaultPage = PageFactory.initElements(this.driver,
      DefaultPage.class);
assertTrue(defaultPage.isUserCreateMenuLinkPresent());

页面对象:

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

但我无法相信这种 try/catch 是人们应该做的事情。 那么检查元素是否存在(使用页面对象)的更好方法是什么?

How to check if an Element exists, when using Page Objects with webdriver.

So far I am doing it this way.

DefaultPage defaultPage = PageFactory.initElements(this.driver,
      DefaultPage.class);
assertTrue(defaultPage.isUserCreateMenuLinkPresent());

Page Object:

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

But I can not believe that this try/catch is the way one should do it. So what would be a better way to check if the elements exits (with using Page Objects)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

吖咩 2024-11-24 15:38:43

问题在于模式本身。它使用 @FindBy 注释(PageFactory 使用它来初始化必须由 Proxy 包装的字段),将标准元素替换为其包含 InvocableHandler 的代理实例。

每次尝试访问用 @FindBy 注释的字段时,调用处理程序都会尝试使用默认的 ElementLocator 查找元素。问题是,如果 ElementLocator.findElement() 方法中没有出现任何元素,则会抛出 TimeoutException / NoSuchElementException 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);
}

因此,每次需要检查元素是否显示时,都必须搜索元素列表并检查其大小。

@FindBy(css = "div.custom")
private List<WebElement> elements
...

public isElementPresented(){
   return elements != null && elements.size > 0
}

解决此问题的另一种方法是创建您自己的 LocatingElementHandlerElementLocator 实现。

因此,如果您需要自己的 isDisplayed() 方法返回 false 而不是 Exception,那么您可以将 ElementLocator 中的 findElement() 方法替换为类似的内容:

...
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
...

并向 LocatingElementHandler.invoke() 添加新条件,

例如:

element = locator.findElement()
if(element == null){
   if(method.name == "isDisplayed"){
      return false
   }
}

The problem is the pattern itself. It uses @FindBy annotation (used by PageFactory to init the fields that must be wrapped by Proxy) that replaces the standard elements with their proxy instances which contain InvocationHandler.

Each time you try to access a field, annotated with @FindBy, the invocation handler tries to find the element using the default ElementLocator.The problem is that the ElementLocator.findElement() method throws an TimeoutException / NoSuchElementException if there are no elements presented in the 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);
}

Therefore, each time you need to check whether an element is displayed or not you have to search for a List of elements and check its size.

@FindBy(css = "div.custom")
private List<WebElement> elements
...

public isElementPresented(){
   return elements != null && elements.size > 0
}

Another way to solve this problem is to create your own implementation of LocatingElementHandler and ElementLocator

So, if you need your own isDisplayed() method to return false instead of Exception, you have to replace the findElement() method in ElementLocator with something like that:

...
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
...

And add new conditions to LocatingElementHandler.invoke()

Something like:

element = locator.findElement()
if(element == null){
   if(method.name == "isDisplayed"){
      return false
   }
}
温柔一刀 2024-11-24 15:38:43

Webdriver 被设计为在未找到元素时抛出异常,因此没有任何方法可以验证 Webdriver 中元素是否存在。

检查此 - http://groups.google.com/group/webdriver/browse_thread/线程/909a9b6cb568e341

Webdriver is designed to throw an exception if an element is not found, So there aren't any methods to verify presence of an element in Webdriver.

Check this - http://groups.google.com/group/webdriver/browse_thread/thread/909a9b6cb568e341

清风夜微凉 2024-11-24 15:38:43

@Ralph:我用同样的方式来做:try/catch。我从来没有找到其他方法。
您可以替换超类中的try/catch 块并将其设计为通用。换句话说:您可以编写一个需要 WebElement 类型的对象的方法。这个方法包含try/catch块并返回true/false...

所以我在测试框架超类中编写了以下公共方法,现在能够在每个页面对象中使用它:

public boolean isElementExisting(WebElement we) {
    try {
        we.isDisplayed();
        return true;
    } catch(NoSuchElementException e) {
        LOGGER.severe("Element does not exist.");
        return false;
    }
}

我不知道为什么这没有在WebDriver中实现...

否则你可以使用WebDriverWait

@Ralph: I do it the same way: try/catch. I've never found another way.
You could swap out the try/catch block in a super class and design it generic. In other words: You could write a method which expects an object of type WebElement. This method contains the try/catch block and return true/false...

So I wrote the following public method in the test framework's super class and am now able to use it in every page object:

public boolean isElementExisting(WebElement we) {
    try {
        we.isDisplayed();
        return true;
    } catch(NoSuchElementException e) {
        LOGGER.severe("Element does not exist.");
        return false;
    }
}

I don't know why this is not implemented in WebDriver...

Otherwise you could use WebDriverWait.

猫弦 2024-11-24 15:38:43

我正在使用这种模式,对我来说效果很好:

public void login() 
{
    if (!loginButton.isDisplayed())
    {
        throw new IllegalStateException("Login button is not displayed!");
    } else
    {
        loginButton.click();    
    }        
}

或者:

public boolean loginButtinIsDisplayed() {
    try {
        this.loginButton.getTagName();
        return true;
    } catch (NoSuchElementException e) {
        e.printStackTrace();
        return false;
    }
}

I'm using this pattern, works fine for me:

public void login() 
{
    if (!loginButton.isDisplayed())
    {
        throw new IllegalStateException("Login button is not displayed!");
    } else
    {
        loginButton.click();    
    }        
}

or:

public boolean loginButtinIsDisplayed() {
    try {
        this.loginButton.getTagName();
        return true;
    } catch (NoSuchElementException e) {
        e.printStackTrace();
        return false;
    }
}
玉环 2024-11-24 15:38:43

我最近看到了这篇旧帖子,相信我找到了一个解决方案。

我正在测试一个带有添加用户按钮的页面。单击该按钮时,会出现各种可编辑文本字段(名字、姓氏、电子邮件等)和一个下拉列表。

单击Cancel 按钮后,字段消失并且不再存在。将 WebDriverWaitExpectedConditions.visibilityOf() 一起使用将不起作用,因为元素不再存在于 DOM 中。

我发现 @FindAll 对我来说是一个解决方案,尽管我必须承认我的测试在我的 List 断言中运行明显缓慢。

对于您的代码,如下所示:

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

不过,我可以在自己的测试中使用类似的东西,并且这似乎是避免“没有此类元素”异常的可靠方法。它基本上是断言的页面对象改编:driver.findElements(By.locator).size() driver.findElements(By.locator).size() driver.findElements(By.locator).size() 1..

I recently came across this old post and believe I've found one solution.

I was testing a page that had an Add User button. When the button was clicked, various editable text fields appeared (for First Name, Last Name, Email, etc..) and a single dropdown.

When a Cancel button was clicked, the fields disappeared and no longer existed. Using WebDriverWait with ExpectedConditions.visibilityOf() would not work since the elements no longer existed in the DOM.

I found that @FindAll was a solution for me, though I must admit my test ran noticeably slow at the my List assertion.

For your code, something like this:

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

I am able to use something similar in my own tests though, and it seems like a dependable way to skirt the 'No such element' exception. It's basically a page object adaptation of asserting: driver.findElements(By.locator).size() < 1.

撩动你心 2024-11-24 15:38:43

这是访问页面上可选元素的一个很好的模式:

@FindBy(...)
private List<WebElement> element;

public Optional<WebElement> getElement() {
    return element.stream().findFirst();
}

在测试中,您可以使用以下断言:

assertEquals(Optional.empty(), page.getElement()); // element should be absent

assertTrue(page.getElement().isPresent()); // element should be present

var e = page.getElement().orElseThrow(AssertionFailedError::new); // check and use element

Here is a nice pattern to access optional elements on a page:

@FindBy(...)
private List<WebElement> element;

public Optional<WebElement> getElement() {
    return element.stream().findFirst();
}

In a test, you could then use the following assertions:

assertEquals(Optional.empty(), page.getElement()); // element should be absent

assertTrue(page.getElement().isPresent()); // element should be present

var e = page.getElement().orElseThrow(AssertionFailedError::new); // check and use element
筑梦 2024-11-24 15:38:43

Arquillian 已在 石墨烯扩展。

检查 ElementLocatorConditionFactory.isPresent() 函数。

他们或多或少做了您在问题中所写的内容(来自 ExpectedConditions.findElement 中硒支持.jar):

try {
    return driver.findElement(by);
} catch (NoSuchElementException e) {
    throw e;
} catch (WebDriverException e) {
    // [...] some log
    throw e;
}

Arquillian has implemented that feature in Graphene extension.

Check ElementLocatorConditionFactory.isPresent() function.

They more or less do what you wrote in your question (from ExpectedConditions.findElement in selenium-support.jar) :

try {
    return driver.findElement(by);
} catch (NoSuchElementException e) {
    throw e;
} catch (WebDriverException e) {
    // [...] some log
    throw e;
}
泪眸﹌ 2024-11-24 15:38:43

使用 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();
    }
}

您告诉 Selenium 获取与该 Id 匹配的所有元素并将它们放入 IWebElement 列表中。然后,您可以在列表上调用 .Any(),如果至少找到一个 IWebElement,则该列表的计算结果为 true。

Using C# bindings:

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

You're telling Selenium to grab all elements that match that Id and put them into a List of IWebElement. You then call .Any() on the list which evaluates to true if at least one IWebElement was found.

落在眉间の轻吻 2024-11-24 15:38:43

尝试一下,这在 pom 中绝对可以工作,

public boolean isPrebuiltTestButtonVisible() {
    try {

        if (preBuiltTestButton.isEnabled()) {

            return true;

        } else {

            return false;
        }

    } catch (Exception e) {

        e.printStackTrace();
        return false;
    }
}

这肯定可以在带有 try catch 的页面对象模型中工作

try this is defiantly work in pom

public boolean isPrebuiltTestButtonVisible() {
    try {

        if (preBuiltTestButton.isEnabled()) {

            return true;

        } else {

            return false;
        }

    } catch (Exception e) {

        e.printStackTrace();
        return false;
    }
}

this will definitely work in page object model surround with try catch

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文