What is the best way to avoid NoSuchElementException in Selenium?
Asked Answered
S

10

24

I have written few test cases in Selenium WebDriver using Java and execute them on grid (hub and multiple nodes). I have noticed that a few test cases fail due to NoSuchElementException. What is the best and robust way to avoid NoSuchElementException and ensure the element is always found?

Stanford answered 23/10, 2013 at 8:44 Comment(0)
S
34

You can never be sure that element will be found, actually this is purpose of functional tests - to tell you if anything changed on your page. But one thing which definitely helps is to add waits for the elements which are often causing NoSuchElementException like

WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id<locator>));
Statolatry answered 23/10, 2013 at 8:50 Comment(3)
This is certainly helpful. Can we have a generic wait condition so that we don't need to check which ExpectedConditions condition to use?Stanford
There is also access to the implicit wait offered by the Webdriver API but as this just sets an arbitrary period of 'do nothing', very similar to a sleep it is encouraged to use the explicit wait, as above for reliability and speed.Jedthus
This is actually the cleanest and simplest code I've seen out of all the code samples. Thanks!Tuna
P
8

I completely agree to Petr Mensik above. The matter you can never say whether element is present. You should clearly understand why when it happens. From my experience I should say that it happens due to the following reasons:

  • 1) The page is still being rendered and you've already finished your element search and obtain no element exception.
  • 2) The second reason is AJAX has not returned yet and you've already obtain NoSuchElementException
  • 3) The third is most obvious: The element is really not on the page whenever.

so the most robust IMHO way to handle all these three conditions using one function call is to use fluentWait as Amith003 suggested.

so the code be the following:

let ur element has the locator:

String elLocXpath= "..blablabla";
WebElement myButton= fluentWait(By.xpath(elLocXpath));
myButton.click();

public WebElement fluentWait(final By locator){
        Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
                .withTimeout(30, TimeUnit.SECONDS)

                .pollingEvery(5, TimeUnit.SECONDS)

        .ignoring(org.openqa.selenium.NoSuchElementException.class);
        WebElement foo = wait.until(
                new Function<WebDriver, WebElement>() {
                    public WebElement apply(WebDriver driver) {
                        return driver.findElement(locator);
                    }
                }
        );
        return  foo;
    };

Also if your purpose is robust code wrap fluentWait() with a try{} catch{} block.

Also don't forget about

 public boolean isElementPresent(By selector)
   {

              return driver.findElements(selector).size()>0;
}

that is also useful.

So to conclude all the mentioned if you want to avoid NoElement exception just handle it properly as nobody can ensure in the element presence on the page.

Hope now it is more clear to you. Regards

Phrasal answered 23/10, 2013 at 10:42 Comment(0)
E
4

you can also use FluentWait,

Each FluentWait instance defines the maximum amount of time to wait for a condition, as well as the frequency with which to check the condition.

Furthermore, the user may configure the wait to ignore specific types of exceptions whilst waiting, such as NoSuchElementExceptions when searching for an element on the page.

// Waiting 30 seconds for an element to be present on the page, checking
   // for its presence once every 5 seconds.
   Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
       .withTimeout(30, SECONDS)
       .pollingEvery(5, SECONDS)
       .ignoring(NoSuchElementException.class);

   WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
     public WebElement apply(WebDriver driver) {
       return driver.findElement(By.id("foo"));
     }
   });

Click here for more info

Eveland answered 23/10, 2013 at 9:14 Comment(7)
This is certainly helpful. But can we have a generic apply method... i.e. for example which waits for page to load completely, then finds the element and if the element is not found refreshes the page and this goes on until the timeout of the fluentwait.Stanford
u can try combining "explicit wait surrounded by a try and catch" with fluent wait..Eveland
try{"use explicit wait"} catch(Exception e){refresh your page and use fluent wait}Eveland
Thanks. Do you have a much better way of having a generic method?Stanford
sorry,right now,i think thats the best way that i could think of,that'll fit ur description...Eveland
The link is brokenMonnet
please what should I import?Monnet
S
3

We can apply below codes to remove this exception condition

  1. By applying WebDriverWait, webdriver object wait for a specific time (in second) of an element for its visibility.

          WebDriverWait wait = new WebDriverWait(driver, 10);       
           wait.until(ExpectedConditions.visibilityOf(link));
    
  2. We can handle NoSuchElementException through try-catch block inside Generic method

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

http://selenium-code.blogspot.in/2017/08/selenium-exception-nosuchelementexcepti.html

Secund answered 15/9, 2017 at 9:54 Comment(0)
P
2
public WebElement fluientWaitforElement(WebElement element, int timoutSec, int pollingSec) {

    FluentWait<WebDriver> fWait = new FluentWait<WebDriver>(driver).withTimeout(timoutSec, TimeUnit.SECONDS)
    .pollingEvery(pollingSec, TimeUnit.SECONDS)
    .ignoring(NoSuchElementException.class, TimeoutException.class);

    for (int i = 0; i < 2; i++) {
        try {
            //fWait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//*[@id='reportmanager-wrapper']/div[1]/div[2]/ul/li/span[3]/i[@data-original--title='We are processing through trillions of data events, this insight may take more than 15 minutes to complete.']")));
            fWait.until(ExpectedConditions.visibilityOf(element));
            fWait.until(ExpectedConditions.elementToBeClickable(element));
        } 
        catch (Exception e) {

            System.out.println("Element Not found trying again - " + element.toString().substring(70));
            e.printStackTrace();
        }
    }

    return element;
}
Philippi answered 19/8, 2016 at 11:27 Comment(0)
B
1
WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
wait.until(ExpectedConditions.elementToBeClickable(By.id<locator>));

elementToBeClickable waits for Enable and Visible of an Element

Boomer answered 12/11, 2013 at 7:35 Comment(0)
L
1

NoSuchElementException occurs, when the locators (i.e. id / xpath/ css selectors) is unable to find the web element on the web page.

The reasons for this could be :

  1. Incorrect Locator

  2. Web element not available on web page

    In order to avoid this exception, we can use Fluent Wait. This wait allows us to define max timeout, polling frequency and define which exception to ignore.

Please find the sample usage of Fluent wait below :

.withTimeout(50, TimeUnit.SECONDS)
.pollingevery(3, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class);
Looney answered 17/7, 2020 at 20:9 Comment(0)
L
0

I usually use this line in the main function

public static void main(String[] args) throws ParseException {
    driver= new ChromeDriver();
    driver.manage().window().maximize();
    **driver.manage().timeouts().implicitlyWait(30,TimeUnit.SECONDS);**

Hope this helps.

Lustreware answered 21/2, 2017 at 0:42 Comment(0)
T
0

Sometimes it is possible to wait for the download of the desired item.

driver.get("https://zzzzzzzzz.market/items/mirage_prime_set")

WebDriverWait(driver, 20)
       .until(
        EC.visibility_of_element_located(
          (By.XPATH, ('//div[@class="orders-row__element order__price sell_color"]')
        )))

Sometimes you need to do something so that the UI framework loads the data. For example scroll page

driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

And then get the necessary data

responsetext=driver.page_source

from lxml import html
parsed_body = html.fromstring(responsetext)

obj1 = parsed_body.xpath('.//div[@class="orders-row__element order__price sell_color"]/span[1]')
print(len(obj1))
Transport answered 2/5, 2019 at 8:1 Comment(0)
E
0

There could be multiple reasons why your locator is failing. The context of your test is important before you go for a solution. In any case, the below steps should be helpful for you.

  • Your locator should NOT be incorrect. If it has changed, then the failing test is correctly pointing towards the recent change.
  • If your locator is correct but still fails or sometimes fails and sometimes passes, then I would suggest a generic method to solve it.

There could be situations where the web element gets detached or is not loaded in the DOM, even if it may look like the whole page or a specific component is loaded.

So use the below method to solve it.

  public WebElement relocateWebElement(By by, WebElement element){
    try {
    
      wait.ignoreAll(Collections.singleton(NoSuchElementException.class))
              .until(refreshed(visibilityOf(element)));

      logger.info(("Element is available. ").concat(element.toString()));

    } catch (NoSuchElementException exception) {
    
      logger.warn(exception.getMessage());
    } 
    
    return driver.findElement(by);
  }
Elevate answered 24/4, 2022 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.