AMA: C# WebDriver Questions

omayer asks…

how to use test data from xlsx in c# webdriver, thank you in advance.

My response…

I haven’t done this as I’ve managed data/scenarios using SpecFlow, but there’s this post that should help you.

omayer also asks…

Handling popup Windows in C# WebDriver – Closing the popup window , finding elements on popup window, locating element inside popup window and frameset

My response…

Stackoverflow is a good resource for these types of questions; there’s some examples on how to do this here.

pallavi asks…

Which tool can we use for report generation in selenium with C#

My response…

This very much depends on which framework you are using to manage your tests. I have used SpecFlow which allows a formatted HTML report of test results.

pallavi also asks…

Can we use selenium with coded ui in visual studio? If I use with coded ui then what template it will follow

My response…

My experience with CodedUI tests in C# has only been negative. When I looked at it, the approach Microsoft used was to generate a huge number of lines of CodedUI test code which I couldn’t understand or make into reusable objects. I much prefer Selenium/WebDriver for any web based tests, and to use White to test any non-web based UIs. I am not familiar with CodedUI templates since it has been a long time since I looked at it.

 

AMA: SpecDriver license?

Erick Hagstrom asks…

I just stumbled across SpecDriver. Looks promising, but I don’t see a license file on github. Under what terms is it offered? (I’m hoping that it is free for commercial use. I am investigating options for test automation on behalf of my company.)

My response…

Yes, everything I write on Github publicly is free for any use.

I have added an explicit GPLv3 license to alleviate any licensing concerns.

Aside: I can’t believe it’s been over five years since I created that repo on a whim, it seems to have helped a lot of people and I’ve used that project pattern subsequently many times myself.

Waiting in C# WebDriver

Waiting in WebDriver is the area where I see most teams struggle in writing maintainable/efficient Selenium WebDriver tests, especially with pages that contain a lot of JavaScript and AJAX to dynamically display content. Here I’ll outline some of the waiting approaches and outline which is best to use in different circumstances.

Background

WebDriver by default waits until the page is loaded (using the onload JavaScript event) before it tries to execute commands. If you have content that loads after page load then you’ll need to use an additional form of waiting.

WebDriver offers two styles of waiting: implicit and explicit.

Implicit Waiting

Implicit, or implied waiting involves setting a configuration timeout on the driver object where it will automatically wait up to this amount of time before throwing a NoSuchElementException.

The benefit of implicit waiting is that you don’t need to write code in multiple places to wait as it does it automatically for you.

The downsides to implicit waiting include unnecessary waiting when doing negative existence assertions and having tests that are slow to fail when a true failure occurs (opposite of ‘fail fast’).

Another consideration is that Even if you set an lower implicit wait, there may be places in your web application where you’ll need a longer explicit wait so it’s hard to use implicit waits exclusively unless you set it at a high value, which is problematic for reasons mentioned above.

Explicit Waiting

Explicit waiting involves putting explicit waiting code in your tests in areas where you know that it will take some time for an element to appear/disappear or change.

The most basic form of explicit waiting is putting a sleep statement in your WebDriver code. This should be avoided at all costs as it will always sleep and easily blow out test execution times.

WebDriver provides a WebDriverWait class which allows you to wait for an element in your code. For example, using the ElementIsPresent extension method I previously wrote about we can do this:

var driver = new FirefoxDriver();
driver.Navigate().GoToUrl("data:text/html,<script> var timer; function startCount() { timer=setInterval(count,1000); } function count() { var e=document.getElementById('counter'); var newVal=parseFloat(e.innerHTML)+1;e.innerHTML=newVal; e.className=\"c\"+newVal;}</script><body onload=\"startCount();\"><div id=\"counter\">1</div></body>");
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
Assert.AreEqual(true, wait.Until(d => d.ElementIsPresent(By.ClassName("c2"))));
Assert.AreEqual(true, wait.Until(d => !d.ElementIsPresent(By.ClassName("c2"))));

You can easily write an extension method that makes it easy to call something like WaitUntilElementIsPresent instead of creating creating new WebDriverWait classes constantly.

public static bool WaitUntilElementIsPresent(this IWebDriver driver, By by, int timeout=10)
{
    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeout));
    return wait.Until(d => d.ElementIsPresent(by));
}

The one issue with using the WebDriverWait class it you can’t use it without throwing an exception when it doesn’t succeed. If you wish to wait without throwing an exception then it is trivial to write your own extension method to do the waiting/polling, such as:

public static bool WaitUntilElementIsPresent(this IWebDriver driver, By by, int timeout=10)
{
    for (var i = 0; i < timeout; i++)
    {
        if (driver.ElementIsPresent(by)) return true;
        Thread.Sleep(1000);
    }
    return false;
}

Recommendations

I suggest you set a small (1 or 2 seconds) implicit wait for your WebDriver code, but ensure that you disable it for any immediate checks, such as if you use an ElementIsPresent method. This way you get free automatic simple waiting with the ability for your tests to still fail fast.

I suggest you add explicit waits in places where it consistently takes longer than this time for an element to appear or disappear. You can easily write some WebDriver extension methods that abstract your wait code so that it is simple to call explicit waits from the driver object itself. That gives the added benefit of having a common explicit waiting approach across your code which you could change if you need to in the future.

Checking an element is present in C# WebDriver

Immediately checking that an element is present and displayed in C# WebDriver is a little more difficult than it initially seems.

The first reason it’s a little tricky is WebDriver throws an exception when an element doesn’t exist in the DOM, so if you’re checking that something doesn’t exist then it won’t work. For example:

[TestMethod]
public void FindVisibleElements()
{
    var driver = new FirefoxDriver();
    driver.Navigate().GoToUrl("data:text/html,<span id=\"hello\">Hello</span>");
    Assert.AreEqual(true,driver.FindElement(By.Id("hello")).Displayed); // Visible Works
    driver.Navigate()
        .GoToUrl(
        "data:text/html,<span id=\"hello\" style=\"position:absolute;left:-9000px;width:0;overflow:hidden;\">Hello</span>");
    Assert.AreEqual(false,driver.FindElement(By.Id("hello")).Displayed); // Not Visible Works
    driver.Navigate().GoToUrl("data:text/html,Hello");
    Assert.AreEqual(false,driver.FindElement(By.Id("hello")).Displayed); // Not Present Throws Exception
    driver.Quit();
}

The finally assertion throws an exception when asserting element with id hello doesn’t exist in the empty html content.

Test method FindVisibleElements threw exception: 
OpenQA.Selenium.NoSuchElementException: Unable to locate element: {"method":"id","selector":"hello"}

A way to get around this is to write a IWebDriver extension method to check the presence of and display of an element, including whether it is actually in the DOM at all:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;

namespace WebDriverExtensions
{
    public static class WebElementExtensions
    {
        public static bool ElementIsPresent(this IWebDriver driver, By by)
        {
            try
            {
                return driver.FindElement(by).Displayed;
            }
            catch (NoSuchElementException)
            {
                return false;
            }
        }
    }

    [TestClass]
    public class ExtensionUnitTests
    {
        [TestMethod]
        public void FindVisibleElements()
        {
            var driver = new FirefoxDriver();
            driver.Navigate().GoToUrl("data:text/html,<span id=\"hello\">Hello</span>");
            Assert.AreEqual(true,driver.ElementIsPresent(By.Id("hello"))); // Visible Works
            driver.Navigate()
                .GoToUrl(
                "data:text/html,<span id=\"hello\" style=\"position:absolute;left:-9000px;width:0;overflow:hidden;\">Hello</span>");
            Assert.AreEqual(false,driver.ElementIsPresent(By.Id("hello"))); // Not Visible Works
            driver.Navigate().GoToUrl("data:text/html,Hello");
            Assert.AreEqual(false,driver.ElementIsPresent(By.Id("hello"))); // Not Present Works
            driver.Quit();
        }
    }
}

This does what we want. Hooray. There’s one more thing to be aware of though. It currently runs in 4 seconds total as we’re not specifying an implicit wait. An implicit wait is a way for WebDriver to wait implicitly for elements to appear. It’s often set on the driver to avoid minor timing issues and to avoid having to write explicit waits throughout your code.

If we specify an implicit wait, the test execution time blows out to 14 seconds as the third assertion waits 10 seconds before throwing the exception.

var driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 0, 10)); // 10 seconds
driver.Navigate().GoToUrl("data:text/html,<span id=\"hello\">Hello</span>");
Assert.AreEqual(true,driver.ElementIsPresent(By.Id("hello"))); // Visible Works
driver.Navigate().GoToUrl("data:text/html,<span id=\"hello\" style=\"position:absolute;left:-9000px;width:0;overflow:hidden;\">Hello</span>");
Assert.AreEqual(false,driver.ElementIsPresent(By.Id("hello"))); // Not Visible Works
driver.Navigate().GoToUrl("data:text/html,Hello");
Assert.AreEqual(false,driver.ElementIsPresent(By.Id("hello"))); // Not Present Works
driver.Quit();

If you really need to use an implicit wait, what we can do is temporarily disable it when checking for existence of our element:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;

namespace WebDriverExtensions
{
    public static class Config
    {
        public static readonly TimeSpan ImplicitWait = new TimeSpan(0, 0, 0, 10);
        public static readonly TimeSpan NoWait = new TimeSpan(0, 0, 0, 0);
    }
    
    public static class WebElementExtensions
    {
        public static bool ElementIsPresent(this IWebDriver driver, By by)
        {
            var present = false;
            driver.Manage().Timeouts().ImplicitlyWait(Config.NoWait);
            try
            {
                present = driver.FindElement(by).Displayed;
            }
            catch (NoSuchElementException)
            {
            }
            driver.Manage().Timeouts().ImplicitlyWait(Config.ImplicitWait);
            return present;
        }
    }

    [TestClass]
    public class ExtensionUnitTests
    {
        [TestMethod]
        public void FindVisibleElements()
        {
            var driver = new FirefoxDriver();
            driver.Manage().Timeouts().ImplicitlyWait(Config.ImplicitWait);
            driver.Navigate().GoToUrl("data:text/html,<span id=\"hello\">Hello</span>");
            Assert.AreEqual(true,driver.ElementIsPresent(By.Id("hello"))); // Visible Works
            driver.Navigate()
                .GoToUrl(
                "data:text/html,<span id=\"hello\" style=\"position:absolute;left:-9000px;width:0;overflow:hidden;\">Hello</span>");
            Assert.AreEqual(false,driver.ElementIsPresent(By.Id("hello"))); // Not Visible Works
            driver.Navigate().GoToUrl("data:text/html,Hello");
            Assert.AreEqual(false,driver.ElementIsPresent(By.Id("hello"))); // Not Present Works
            driver.Quit();
        }
    }
}

This takes our test time back to 4 seconds, whilst allowing us to use an implicit wait for the rest of our test suite.

So, there we have it. A reasonably simple extension method on the IWebDriver class that allows us to check the immediate existance of an element in the DOM and displayed without throwing an exception.

Next I will write about how we can use this to write an extension method to explicitly wait for an element that we are expecting to appear (or disappear).

C# WebDriver extension: Driver.TakeScreenshot

I was having a chat to a colleague this week about how Watir in Ruby is much nicer than WebDriver in other languages because it provides a lot more functionality without any work, such as screenshots, waiting etc. I hadn’t really thought about that for a while because, despite working predominantly in C#, I have a collection of WebDriver and WebElement extensions I have written which make it much easier to use.

I plan to write a series of short posts sharing a different extension method in each one.

I previously shared these:

  • IWebElement.Set(text)
  • SelectElement.SelectBySubText(subText)
  • IWebElement.FindVisibleElement(by)
  • IWebElement.VisibleElementExists(by)

Today I will share IWebDriver.TakeScreenshot(prefix). This method allows you to easily take a timestamped screenshot of what is displayed in the browser with an optional prefix to the filename.

using System;
using System.Drawing.Imaging;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;

namespace WebDriverExtensions
{
    public static class WebElementExtensions
    {
        public static string TakeScreenshot(this IWebDriver driver, string prefix)
        {
            var fileName = String.Format("{0}{1}{2}", prefix, DateTime.Now.ToString("HHmmss"), ".png");
            var screenShot = ((ITakesScreenshot)driver).GetScreenshot();
            screenShot.SaveAsFile(fileName, ImageFormat.Png);
            return fileName;
        }
    }

    [TestClass]
    public class ExtensionUnitTests
    {
        [TestMethod]
        public void TakeScreenShot()
        {
            var driver = new FirefoxDriver();
            driver.Navigate().GoToUrl("data:text/html,Some Text");
            var fileName = driver.TakeScreenshot("myScreenShot");
            Assert.IsTrue(File.Exists(fileName));
            driver.Quit();
        }
    }
}

Getting an element’s parent in WebDriver in C#

I was looking for an easy inbuilt way to access an element’s parent in WebDriver in C# today, similar to this method in watir-webdriver:

b.text_field( name: 'q').parent

but I couldn’t find one. This is needed when you have two elements next to each other (siblings), and you want to access one via identifying the other. A classic case is where you have a label element which you use to identify, and then want to get its input sibling to enter some text.

sample html

The only way I could do this in WebDriver was to write a C# extension method that uses an xpath expression to locate the parent element

public static IWebElement GetParent(this IWebElement e)
{
return e.FindElement(By.XPath(".."));
}

I believe it would be useful to have this built into WebDriver but I won’t be holding my breath for it to happen as according to Simon Stewart:

“It was left out because webdriver isn’t a tool for doing random walks of a webpage and because there are already mechanisms to get hold of the parent element that don’t add additional methods to one of the core interfaces.”

So, I’m wondering. Is this xpath really the only way to get an element’s parent? Is there a better way? Would you find this useful to be in built?

Web element extensions in C# that I find useful

I really miss the Watir API when working with WebDriver in C#. Whilst the C# WebDriver bindings are fantastic (thanks Se team!), simple things I like to do with the Watir API aren’t present, so I find myself writing web element extension methods to make my life easier (and code cleaner). Here’s some of my favourites: (wouldn’t it be neat to see them in WebDriver one day. I can dream…)

IWebElement.Set(text)

I have always found .SendKeys to be an unusual method, as I haven’t come across an instance where I want to fire keys at a field instead of just setting it to a value. So .Set(text) is usually one of the first web element extensions that I write:

namespace WebDriverExtensions
{
  using Microsoft.VisualStudio.TestTools.UnitTesting;
  using OpenQA.Selenium;
  using OpenQA.Selenium.Firefox;

  public static class WebElementExtensions
  {
    public static void Set(this IWebElement e, string text)
    {
      e.Clear();
      e.SendKeys(text);
    }
  }

  [TestClass]
  public class ExtensionUnitTests
  {
    [TestMethod]
    public void SetTextField()
    {
      var driver = new FirefoxDriver();
      driver.Navigate().GoToUrl("data:text/html,<input type=\"text\" id=\"t\" />");
      var textField = driver.FindElement(By.Id("t"));
      textField.SendKeys("a");
      Assert.AreEqual("a", textField.GetAttribute("value"));
      textField.SendKeys("b");
      Assert.AreEqual("ab", textField.GetAttribute("value"));
      textField.Set("b");
      Assert.AreEqual("b", textField.GetAttribute("value"));
      driver.Quit();
    }
  }
}

SelectElement.SelectBySubText(subText)

The Selenium Support library provides some useful select element specific methods, but unfortunately the .SelectByText(partial) method doesn’t select by subtext (as advertised), so I wrote a .SelectBySubText(subText) method to actually do this.

namespace WebDriverExtensions
{
  using System.Linq;
  using Microsoft.VisualStudio.TestTools.UnitTesting;
  using OpenQA.Selenium;
  using OpenQA.Selenium.Firefox;
  using OpenQA.Selenium.Support.UI;

  public static class WebElementExtensions
  {
    public static string SelectBySubText(this SelectElement me, string subText)
    {
      foreach (var option in me.Options.Where(option =&gt; option.Text.Contains(subText)))
      {
        var optionText = option.Text;
        option.Click();
        return optionText;
      }
      me.SelectByText(subText);
      return subText;
    }
  }

  public class ExtensionUnitTests
  {
    [TestMethod]
    public void SelectBySubTextTest()
    {
      var driver = new FirefoxDriver();
      driver.Navigate().GoToUrl("data:text/html,VolvoSaab");
      var selectList = new SelectElement(driver.FindElement(By.Id("cars")));
      selectList.SelectByText("Volvo");
      Assert.AreEqual(selectList.SelectedOption.Text, "Volvo");
      selectList.SelectBySubText("Saa");
      Assert.AreEqual(selectList.SelectedOption.Text, "Saab");
      driver.Quit();
    }
  }
}

IWebElement.FindVisibleElement(by) and IWebElement.VisibleElementExists(by)

Web Apps I test often hide elements that are available to use, but unfortunately WebDriver still locates these (even though it won’t allow you to interact with them). So, I wrote two extensions: one to only find an element matching a selector that is visible, and another to check if a visible element exists matching a selector.

namespace WebDriverExtensions
{
  using System;
  using System.Linq;
  using Microsoft.VisualStudio.TestTools.UnitTesting;
  using OpenQA.Selenium;
  using OpenQA.Selenium.Firefox;

  public static class WebElementExtensions
  {
    public static IWebElement FindVisibleElement(this IWebDriver driver, By by)
    {
      var elements = driver.FindElements(by);
      foreach (var element in elements.Where(e => e.Displayed))
      {
        return element;
      }
      throw new NoSuchElementException("Unable to find visible element with " + @by);
    }

    public static bool VisibleElementExists(this IWebDriver driver, By by, Int32 implicitWait=10)
    {
      driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 0));
      var elements = driver.FindElements(by);
      var visibleElements = elements.Count(e => e.Displayed);
      driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, implicitWait));
      return visibleElements != 0;
    }
  }

  public class ExtensionUnitTests
  {
    [TestMethod]
    public void FindVisibleElements()
    {
      var driver = new FirefoxDriver();
      driver.Navigate().GoToUrl("data:text/html,<input  type=\"hidden\" class=\"myfield\" name=\"date-submitted\" value=\"Hidden\"><input class=\"myfield\" name=\"date-submitted\" value=\"Visible\">");
      var textField = driver.FindElement(By.ClassName("myfield"));
      Assert.IsFalse(textField.Displayed);
      var visibleTextField = driver.FindVisibleElement(By.ClassName("myfield"));
      Assert.IsTrue(visibleTextField.Displayed);
      Assert.AreEqual("Visible", visibleTextField.GetAttribute("value"));
      driver.Quit();
    }

    [TestMethod]
    public void VisibleElementExists()
    {
      var driver = new FirefoxDriver();
      driver.Navigate().GoToUrl("data:text/html,<input  type=\"hidden\" class=\"myfield\" name=\"date-submitted\" value=\"Hidden\"><input class=\"myfield\" name=\"date-submitted\" value=\"Visible\">");
      Assert.IsTrue(driver.VisibleElementExists(By.ClassName("myfield")));
      driver.Quit();
    }

    [TestMethod]
    public void VisibleElementDoesNotExist()
    {
      var driver = new FirefoxDriver();
      driver.Navigate().GoToUrl("data:text/html,<input  type=\"hidden\" class=\"myfield\" name=\"date-submitted\" value=\"Hidden\">");
      Assert.IsFalse(driver.VisibleElementExists(By.ClassName("myfield")));
      driver.Quit();
    }
  }
}

Summary

These are some web element extensions that I find useful. Do you have any? What would you like to see built into WebDriver?