When switching from Selenium 1 to Selenium 2/WebDriver you will notice bitterly that there is no isElementPresent()-method anymore.
Solutions found on the Internet
To fix that issue, one of the most shown solutions on the Internet is the usage of driver#findElement(By) and a try-/catch-statement:
public boolean isElementPresent(String id) { boolean present; try { driver.findElement(By.id(id)); present = true; } catch (NoSuchElementException e) { present = false; } return present; }
Not very elegant. In addition the JavaDoc of WebElement#findElementy(By) states explicitly, that this method shouldn’t be used “to look for non-present elements”. Instead WebElement#findElements(By) should be used:
public boolean isElementPresent(String id) { return driver.findElements(By.id(id)).size() != 0; }
Although this solution avoids the need of the try/catch-statement, it still lacks in the timeout/implicit waiting functionality Selenium 2 ships with. Every time this method is called, the WebDriver tries to find the element until the given timeout is reached. Therefore our test would wait a certain period of time (to be precise the value set by implicitlyWait) whenever this method is about to return ‘false’.
To get ahead of that, you can explicitly set the timeout:
public boolean isElementPresent(String id, WebDriver driver) { boolean present; driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS); boolean present = driver.findElements( By.id(id) ).size() != 0; driver.manage().timeouts().implicitlyWait(1000, TimeUnit.SECONDS); return present; }
Problem when using the Page-Object-Pattern
The big dissapointment: when using the Page-Object-Pattern in conjunction with the @FindBy-Annotation you are constrained to implement the WebElement’s id at least twice:
@FindBy(id="fooId") private WebElement webelement; public void testSome() { ... isElementPresent("fooId", driver); ... }
So if “fooId” changes its value, you have to fix that in your code on multiple positions. That should be avoided.
A (half-way clean) solution
I tried to overcome that problem by passing the WebElement directly to my isElementPresent-Method. As a result, I’m able to hold the id on one single position in my code. As a drawback (or advantage) I’m unable to use the id in my isElementPresent-Method anymore. This is caused by the absence of a suitable way in the WebElement’s API to get the by-value (eg. By.id(“foo”)) which is used to find the element. Therefore using WebDriver#findElements(By) gone out of reach.
Finally I step back to the solution I preliminary declared as “not very elegant”: I use a try-/catch-statement in which I call a method on the webelement which throws an NoSuchElementException in case the element is not present:
public static boolean isElementPresent(WebDriver webdriver, WebElement webelement) { boolean exists = false; webdriver.manage().timeouts().implicitlyWait(0, TimeUnit.MILLISECONDS); try { webelement.getTagName(); exists = true; } catch (NoSuchElementException e) { // nothing to do. } webdriver.manage().timeouts().implicitlyWait(1000, TimeUnit.MILLISECONDS); return exists; }
Note:
- If I have to deliberate about whether avoiding the try/catch-statement or avoiding to code the ids mulitple times, I actually prefer the last one.
- If your value for implicitly wait differs from 1000, calling this method will obviously overwrite the current value.