22

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)?

Ralph
  • 118,862
  • 56
  • 287
  • 383
  • If your problem is to check for the element visibility, this could help : http://stackoverflow.com/questions/2646195/how-to-check-if-an-element-is-visible-with-webdriver – phtrivier Jun 30 '11 at 11:34
  • @phtrivier: No this does not work, because the question is not about Visible, it is really about existence. – Ralph Jun 30 '11 at 11:40
  • Would getting another element injected by the annotation (one that exists like the root) and using rootELement.findElements(ByWhatever).isEmpty() be acceptable ? – phtrivier Jun 30 '11 at 11:48
  • @phtrivier: I am searching for a easy solution (for an easy problem). I want simple reduce the amount of code, and do not want to write someting like an id twice. – Ralph Jun 30 '11 at 12:20

9 Answers9

5

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
   }
}
codefan-BK
  • 310
  • 2
  • 9
Andrey Shkrob
  • 51
  • 2
  • 4
  • When you check the size of the elements list, won't it have the proxy instances and counted as non-empty? – Eugene S Nov 02 '15 at 08:01
  • 1
    @EugeneS No, it will be WebElements, it's the meaning of proxies to redirect all element invocations to ElementLocator. And then ElementLocator interacting with concrete Context and WebElements – Andrey Shkrob Jan 19 '16 at 20:35
  • In you `isElementPresented()` example I don't think the element will be `null` even if no actual element was found. It will still be a proxy object that won't be equal to `null`. – Eugene S Apr 01 '16 at 03:22
4

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

dmp
  • 374
  • 1
  • 4
  • 11
  • 3
    So what you mean is: that there is no way to check the existence of an Web Element in an PAGE OBEJECT, except try to invoke a method on an Proxy that may fail, because there is no instance behind this Proxy? – Ralph Jul 01 '11 at 11:06
  • Whilst this may theoretically answer the question, [it would be preferable](//meta.stackoverflow.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – Kalle Richter Oct 13 '17 at 06:44
2

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

Flo Bayer
  • 1,190
  • 4
  • 12
  • 25
  • 1
    +1 for using common code, but sadly it doesn't answer the OP's hope for a way to do it without using try-catch. – Vince Bowdren Apr 14 '15 at 16:19
  • Thanks. Well, I hope the Selenium guys will implement a function for that. But in my opinion that's a reasonable solution for now. – Flo Bayer Apr 16 '15 at 08:56
1

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;
    }
}
prs
  • 19
  • 1
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.

Newd
  • 2,174
  • 2
  • 17
  • 31
T.D.
  • 73
  • 7
1

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
rolve
  • 10,083
  • 4
  • 55
  • 75
0

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;
}
Anthony O.
  • 22,041
  • 18
  • 107
  • 163
0

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.

Pete
  • 3,842
  • 3
  • 31
  • 42
-1

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

  • This is about checking the "enabled" flag, but it will fail when the element does not exist - and the question is about existence - sorry. – Ralph Oct 19 '14 at 09:59
  • This uses a try-catch; the OP is asking if there is a solution which does _not_ use try-catch. – Vince Bowdren Apr 14 '15 at 16:18