On several sites I have worked on in recent years there has always been a check scenario whereby I have wanted to check that something isn’t on the page, such as a error message or a field not required in a given context.

WebDriver by design, as its intended to show you what the user can see, will return a NoSuchElementException if given a locator for an element that shouldn’t be on the page.

So what most people write is a function containing a try catch and subsequently return a bool indicating if the element is on the page or not. Something like this:

public bool IsTestElementPresent(IWebDriver driver)
{
try
{
driver.FindElement(By.Id("Test"));
return true;
}
catch (NoSuchElementException)
{
return false;
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

In my opinion this is a nice way to do it, you could of course return the exception and assert against that, but I find bool a nicer approach. But what is often overlooked with this approach is the default timeout for the driver. If you haven’t altered this, then it will be 0 so you won’t have this problem, but I know a lot of people do to reduce flakiness, so lets says its 20 seconds. What happens when you run the above code is WebDriver will try to find the element for that time duration, making it look like your test has hung, before it declares it not present. This can be a lengthy amount of time depending how many times you are looking for something not to be present during your suite.

One way to achieve this is to reduce the driver timeout before the try catch and then setting it back to the appropriate value afterwards. This could be done in a helper class or if you have created a custom driver can be added as a method on that. Something like this:

public class Extensions
{
public bool IsElementPresent(IWebDriver driver, By locator)
{
//Set the timeout to something low
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(100));
try
{
driver.FindElement(locator);
//If element is found set the timeout back and return true
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(20000));
return true;
}
catch (NoSuchElementException)
{
//If element isn't found, set the timeout and return false
driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(20000));
return false;
}
}
}
//How to use
public class Tests
{
private IWebDriver _driver = new ChromeDriver();
public void Test1()
{
var isElementPresent = new Extensions().IsElementPresent(_driver, By.Id("Test"));
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

Another way to do this is with the IDisposable interface as introduce to me by a chap called James Barker, use the using command and then do your call inside there, then the timeout would be automatically set back after the call during the disposal. Something like this:

public class DriverImplicitWait : IDisposable
{
private readonly IWebDriver _driver;
public DriverImplicitWait(IWebDriver driver, int waitMilliseconds)
{
_driver = driver;
//Change the timeout to the value passed in
_driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(waitMilliseconds));
}
//When the using statement ends
public void Dispose()
{
//Set the timeout back to the default timeout
_driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(Settings.DefaultTimeout));
}
}
public class TestSuite
{
private IWebDriver _driver = new ChromeDriver();
public bool ExampleUsage()
{
//The using statement will create the new object, but once the code leaves the using statement
//it will set the driver timeout back to the default from the settings file
using (new DriverImplicitWait(_driver, 100))
{
try
{
_driver.FindElement(By.Id("Test"));
return true;
}
catch (NoSuchElementException)
{
return false;
}
};
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

However if you are following the PageObject approach and using the PageFactory then you would want to be passing the appropriate IWebElement in to the method. You would initially think that this isn’t possible because the element would be null, but it actually isn’t because at the point of initialising the PageObject with the factory is creates it as an IWebElementProxy (something like this, some black magic :) ) so you can actually pass the IWebElement to a method and call the IsDisplayed() method inside a try catch like above.

I achieve this with the following code, but note that this code isn’t full proof because if the element is present but not displayed you will get false, if the element isn’t present you will get false. So if you intention is to check that the element isn’t in the code at all, your probably better following a pattern above. Issue there will be you will likely have to duplicate your locator or you could do some nasty reflection to get the locator, or stick it in a const string. However I haven’t had a need to do that and for me as long as a user cannot see the element, I am happy.

public class DriverImplicitWait : IDisposable
{
private readonly IWebDriver _driver;
public DriverImplicitWait(IWebDriver driver, int waitMilliseconds)
{
_driver = driver;
//Change the timeout to the value passed in
_driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(waitMilliseconds));
}
//When the using statement ends
public void Dispose()
{
//Set the timeout back to the default timeout
_driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(Settings.DefaultTimeout));
}
}
public class WebDriverExtensions
{
private readonly IWebDriver _driver;
public WebDriverExtensions(IWebDriver driver)
{
_driver = driver;
}
public bool IsElementDisplayed(IWebElement element, int withinMilliseconds)
{
//We use the timeout controller class from above or which ever namespace you put it in
using (new DriverImplicitWait(_driver, withinMilliseconds))
{
try
{
//Because we are using the PageFactory at the time of using this element WebDriver will attempt to
//Find the element and check displayed status, if its present and displayed we will get true
//If its present and hidden we will get false
return element.Displayed;
}
catch (NoSuchElementException)
{
//If the element is present at all we get the NoSuchElementExpcetion and return false;
return false;
}
}
}
}
public class TestPage
{
private readonly IWebDriver _driver;
public TestPage(IWebDriver driver)
{
_driver = driver;
PageFactory.InitElements(driver, this);
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.FindElement(By.CssSelector("#something")));
}
[FindsBy(How = How.Id, Using = "test")]
private IWebElement LblTest { get; set; }
public bool IsTestElementDisplayed()
{
return new WebDriverExtensions(_driver).IsElementDisplayed(LblTest, 50);
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

So there you have it, several approaches to dealing with checking if an element is present/displayed.
Hope this is of use to some of you and happy coding!

Update

Here is another approach for you. Jim Holmes asked me and Jim Evans how to check if an element is not present, Jim replied with this very neat approach, which could also be used to check if an element is present too. The reason this works is due to the fact that findElements won’t throw an exception if non are present, the collection is just empty.

Comments