190

How can I check if an element exist with web driver?

Is using a try-catch really the only possible way?

boolean present;
try {
   driver.findElement(By.id("logoutLink"));
   present = true;
} catch (NoSuchElementException e) {
   present = false;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • 2
    @Michael Freidgeim: before you blaim somebody of writing a dublicate question, please check the question's creation date to decide which one is the dublicate. – Ralph Apr 29 '16 at 07:36
  • I don't blame you. It's just a way of housekeeping- to link similar questions together.See [Should I vote to close a duplicate question, even though it's much newer, and has more up to date answers?](http://meta.stackexchange.com/a/147651) "If the new question is a better question or has better answers, then vote to close the old one as a duplicate of the new one." – Michael Freidgeim Apr 29 '16 at 13:26

10 Answers10

329

You could alternatively do:

driver.findElements(By.id("...")).size() != 0

Which saves the nasty try/catch

P.S.:

Or more precisely by @JanHrcek here

!driver.findElements(By.id("...")).isEmpty()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mike Kwan
  • 24,123
  • 12
  • 63
  • 96
65

I agree with Mike's answer, but there's an implicit 3 second wait if no elements are found which can be switched on/off which is useful if you're performing this action a lot:

driver.manage().timeouts().implicitlyWait(0, TimeUnit.MILLISECONDS);
boolean exists = driver.findElements( By.id("...") ).size() != 0
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);

Putting that into a utility method should improve performance if you're running a lot of tests.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Edd
  • 8,402
  • 14
  • 47
  • 73
  • 7
    Because the implicite timeout can be 3 seconds, but also an other value, one sould store the old value first, and then leate reset it. But unfortunaly you can set the value, but not read it -- cool api – Ralph Jan 09 '12 at 15:02
  • 4
    It seems the default implicit wait time is 0, (http://seleniumhq.org/docs/04_webdriver_advanced.html) So unless you've configured it to be longer, this shouldn't be necessary. – Andrew M May 24 '12 at 09:43
  • @Ralph is there a way to get the current value? – Tim Büthe Sep 20 '12 at 16:19
  • At first you set it somewhere, you should store it yourself. The default value is also given. – szab.kel Oct 24 '14 at 22:16
18

As the comment stated, this is in C# not Java but the idea is the same. I've researched this issue extensively and ultimately the issue is, FindElement always returns an exception when the element doesn't exist. There isn't an overloaded option that allows you to get null or anything else. Here is why I prefer this solution over others.

  1. Returning a list of elements then checking if the list size is 0 works but you lose functionality that way. You can't do a .click() on a collection of links even if the collection size is 1.
  2. You could assert that the element exists but often that stops your testing. In some cases, I have an extra link to click depending on how I got to that page and I want to click it if it exists or move on otherwise.
  3. It's only slow if you don't set the timeout driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(0));
  4. It's actually a very simple and elegant once the method is created. By using FindElementSafe instead of FindElement, I don't "see" the ugly try/catch block and I can use a simple Exists method. That would look something like this:

    IWebElement myLink = driver.FindElementSafe(By.Id("myId"));
    if (myLink.Exists)
    {
       myLink.Click();
    }
    

Here is how you extend IWebElement & IWebDriver

IWebDriver.FindElementSafe

    /// <summary>
    /// Same as FindElement only returns null when not found instead of an exception.
    /// </summary>
    /// <param name="driver">current browser instance</param>
    /// <param name="by">The search string for finding element</param>
    /// <returns>Returns element or null if not found</returns>
    public static IWebElement FindElementSafe(this IWebDriver driver, By by)
    {
        try
        {
            return driver.FindElement(by);
        }
        catch (NoSuchElementException)
        {
            return null;
        }
    }

IWebElement.Exists

    /// <summary>
    /// Requires finding element by FindElementSafe(By).
    /// Returns T/F depending on if element is defined or null.
    /// </summary>
    /// <param name="element">Current element</param>
    /// <returns>Returns T/F depending on if element is defined or null.</returns>
    public static bool Exists(this IWebElement element)
    {
        if (element == null)
        { return false; }
        return true;
    }

You could use polymorphism to modify the IWebDriver class instance of FindElement but that's a bad idea from a maintenance standpoint.

Brantley Blanchard
  • 1,208
  • 3
  • 14
  • 23
  • 3
    There are two problems: 1) wrong language: Java not C#, 2) you used the ugly try/catch workaround (and this was the question, if there is an other way than this ugly and slow try/catch) – Ralph Aug 05 '13 at 15:34
  • 5
    Yes, that is C# but the concept is the same. Try/Catch is only ugly if you have to do it often. That's why I encapsulated it out in a separate method. The final solution is actually very pretty. bool exists = driver.FindElementSafe(by).Exists(); – Brantley Blanchard Aug 05 '13 at 20:07
  • Try/Catch is not just ugly in the code, but slow at runtime. Encapsulating it in a different method deals with the first issue, but not the second. – Vince Bowdren Apr 14 '15 at 16:01
  • 10
    lol, UI testing isn't known for speed hence it's the top of the "Agile Testing Pyramid". The fraction of a second lost in a try/catch id dwarfed by the time it takes to get and render a page in Chrome or whatever browser you're using. If you're that worried about fractions of a seconds, you're doing the wrong type of testing. I would suggest looking into doing more unit tests in that case. – Brantley Blanchard Apr 15 '15 at 15:47
7

This works for me every time:

    if(!driver.findElements(By.xpath("//*[@id='submit']")).isEmpty()){
        // Then click on the submit button
    }else{
        // Do something else as submit button is not there
    }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Amstel Bytes
  • 177
  • 1
  • 8
  • From [a comment](https://stackoverflow.com/questions/30002313/selenium-finding-elements-by-class-name-in-python#comment128785684_30025430): *"`find_element_by_*` and `find_elements_by_*` are removed in Selenium 4.3.0. Use `find_element` instead."*. – Peter Mortensen Nov 23 '22 at 02:41
4

I extended the Selenium WebDriver implementation, in my case HtmlUnitDriver to expose a method,

public boolean isElementPresent(By by){}

like this:

  1. check if the page is loaded within a timeout period.
  2. Once the page is loaded, I lower the implicitly wait time of the WebDriver to some milliseconds, in my case 100 milliseconds, but it probably should work with 0 milliseconds too.
  3. call findElements(By). The WebDriver, even if it will not find the element, will wait only the amount of time from above.
  4. rise back the implicitly wait time for future page loading

Here is my code:

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

public class CustomHtmlUnitDriver extends HtmlUnitDriver {

    public static final long DEFAULT_TIMEOUT_SECONDS = 30;
    private long timeout = DEFAULT_TIMEOUT_SECONDS;

    public long getTimeout() {
        return timeout;
    }

    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    public boolean isElementPresent(By by) {
        boolean isPresent = true;
        waitForLoad();
        // Search for elements and check if list is empty
        if (this.findElements(by).isEmpty()) {
            isPresent = false;
        }
        // Rise back implicitly wait time
        this.manage().timeouts().implicitlyWait(timeout, TimeUnit.SECONDS);
        return isPresent;
    }

    public void waitForLoad() {
        ExpectedCondition<Boolean> pageLoadCondition = new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver wd) {
                // This will tel if page is loaded
                return "complete".equals(((JavascriptExecutor) wd).executeScript("return document.readyState"));
            }
        };
        WebDriverWait wait = new WebDriverWait(this, timeout);
        // Wait for page complete
        wait.until(pageLoadCondition);
        // Lower implicitly wait time
        this.manage().timeouts().implicitlyWait(100, TimeUnit.MILLISECONDS);
    }
}

Usage:

CustomHtmlUnitDriver wd = new CustomHtmlUnitDriver();
wd.get("http://example.org");
if (wd.isElementPresent(By.id("Accept"))) {
    wd.findElement(By.id("Accept")).click();
}
else {
    System.out.println("Accept button not found on page");
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lotzy
  • 489
  • 1
  • 6
  • 14
2

You can do an assertion.

See the example

driver.asserts().assertElementFound("Page was not loaded",
By.xpath("//div[@id='actionsContainer']"),Constants.LOOKUP_TIMEOUT);

You can use this this is native:

public static void waitForElementToAppear(Driver driver, By selector, long timeOutInSeconds, String timeOutMessage) {
    try {
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        wait.until(ExpectedConditions.visibilityOfElementLocated(selector));
    } 
    catch (TimeoutException e) {
        throw new IllegalStateException(timeOutMessage);
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ran Adler
  • 3,587
  • 30
  • 27
  • 5
    This seems interesting, but can you clarify what driver is that? The [`WebDriver` javadocs](http://selenium.googlecode.com/git/docs/api/java/index.html) don't show that `asserts()` method. – acdcjunior Aug 30 '13 at 15:02
1

Write the following method using Java:

protected boolean isElementPresent(By by){
    try{
        driver.findElement(by);
        return true;
    }
    catch(NoSuchElementException e){
        return false;
    }
}

Call the above method during an assertion.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ripon Al Wasim
  • 36,924
  • 42
  • 155
  • 176
  • 7
    I do not see how this answer the question: "Is using a try catch really the only possible way?"? – Ralph Mar 16 '15 at 07:30
  • It doesn't answer the question. It only attempts to put the code from the question into a function (essentially rehashing the question). And does it even work? How can the specific string "logoutLink" be passed to it? It *seems* like a completely ***bogus answer***. It at least ought to include an example of how the function is called for this particular question. And include a link to documentation for findElement() to clarify if it takes ***one*** parameter. That is why code should always be explained: What is the intent and how does it work. "Try this" answers are not sufficient. – Peter Mortensen Nov 23 '22 at 02:56
-1
String link = driver.findElement(By.linkText(linkText)).getAttribute("href")

This will give you the link the element is pointing to.

Marko
  • 20,385
  • 13
  • 48
  • 64
  • 1
    but this will result in a Nullpointer exception if the element with text "linkText" does not exist -- it is at the end the same not working idea posted by nu1silva – Ralph Dec 19 '12 at 19:54
-6

With version 2.21.0 of selenium-java.jar you can do this;

driver.findElement(By.id("...")).isDisplayed()
nu1silva
  • 595
  • 4
  • 2
  • 1
    Are you sure: I excpect `driver.findElement(By.id("..."))` to thrown an exception. – Ralph Apr 25 '12 at 12:38
  • 5
    It indeed still throws an exceptio nin 2.21.0 OpenQA.Selenium.NoSuchElementException : Unable to locate element: {"method":"id","selector":"FormButtonPanel_ButtonCancel"} Command duration or timeout: 4 milliseconds For documentation on this error, please visit: http://seleniumhq.org/exceptions/no_such_element.html Build info: version: '2.21.0', revision: '16552', time: '2012-04-11 19:08:38' System info: os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.6.0_22' Driver info: driver.version: EventFiringWebDriver – Sentient May 21 '12 at 22:04
  • 6
    -1 This is wrong. isDisplayed can be used to test if an element is visible right now, not if it exists in the dom. – Tim Büthe Sep 20 '12 at 16:21
  • 1
    -1 This is *not* the same thing as whether an element exists on a page. – joshin4colours Apr 05 '13 at 18:07
-6

As I understand it, this is the default way of using the web driver.

Pål Brattberg
  • 4,568
  • 29
  • 40