Continuous delivery, Programming (back-end)

Get value of Selenium IWebElement

Introduction

Selenium.WebDriver (SeleniumHQ) is a great tool that lets you automate your Web UI Tests. With the tool you can navigate through web pages, find elements, put text into input controls or click links or buttons placed in that pages. Unfortunately as life shows, you can always find some lacks. The lack of element’s value getter is one of them.
 

Example scenario

You build a system where the user can enter data in some popup dialogs. The obvious test you may write will be the test for saving the given data, but another one may be the test checking that after saving the data and reopenning the form all the fields are empty again. Unfortunatelly you cannot use the Text property getter, because you are dealing with the INPUT control (which moreover is changed by JavaScript thus the DOM’s innerHtml property is not set).
 

Solution

You could say that unless the element were IWebElement in Selenium.WebDriver but mere DOM object you could get it’s value using javaScript, i.e. jQuery val() function. Happily Selenium.WebDriver provides a way to execute javaScript in the browser. The idea is to make the element identifiable and execute a script that finds the element and gets the value. Here’s my implementation of that idea (I have put everything to C# extension method):
public static string GetValue(this IWebElement element)
{
//Ensure that there is are identyfiable elements
Contract.Requires(element != null);
Contract.Assert(String.IsNullOrWhiteSpace(element.GetAttribute("id")) == false,
"Empty element 'id' attribute");

//Build selector to get the element by the browser
var cssSelector = String.Format("{0}#{1}",
element.TagName, element.GetAttribute("id"));

//Get WebDriver to look for elements and execute javaScript
var webDriver = ((RemoteWebElement)element).WrappedDriver;

//Ensure that the javaScirpt script finds the exact element
var foundElements = webDriver.FindElements(By.CssSelector(cssSelector));

Contract.Assert(foundElements .Count == 0, 
"No element found using the given selector");
Contract.Assert(foundElements .Count > 1, 
"Unique ID constraint violated for the element");

//Build and execute the javaScript
var script = String.Format(@"return $(""{0}"").val()", cssSelector);
return ((IJavaScriptExecutor)webDriver).ExecuteScript(script) as string;
}
 

The design-by-contract programming takes considerable amount of code here but the idea remains simple. The javaScript line is written as jQuery code but if you want you can write here anything that obtains the element’s value.
 

Disadvantages?

The most fragile part of the solution is grabbing the element. There must be a way to identify it with javaScript – I used element’s ‘id’ attribute because it’s simple, but you can construct XPath expression or anything you want to do that. If you test the software you create, you can always influece the way the element was created and predefine the identifier. For autogenerated elements you may provide a way of getting the element using XPath or your own way of getting the descendant of another identifiable element.
What about performance – are we looking for the same element twice!? Yes, but this could takes miliseconds while the whole session of UI Tests could take tens of minutes. The waste of time is repaid by the new functionality coverage.
 
Happy UI Testing!
Advertisements