by Andreas

Selenium – Working with dynamic ASP.NET controls

Selenium LogoI recently had to find a replacement for our extensive Watin UI test framework. The reason being problems as a result of the IE11 release, and that Watin seems to have been abandonded in April 2011.

The choice landed on Selenium, which seems to be the de facto standard these days. It allows you to test in a variety of browsers, and is very much alive and kicking. There’s a .NET web driver available as a NuGet package, which makes it very easy to get started. But this isn’t a post about how great (or not?) Selenium is, it’s just a tip for those who’s using it for ASP.NET projects and came across the same annoyance as I did.

Selenium doesn’t handle dynamic ASP.NET controlId’s very well. It says in the docs that they recommend using static id’s, but seriously.. For projects before ClientIdMode was supported (< 4.0) this simply wasn’t an option. And should we go through thousands of lines of code and change this? I think not.

The solution is using XPath. Which, in Selenium’s defence, is supported. But it’s ugly, compared to using RegEx which was supported by Watin. Take the following example:

[html]<tr>
<td>
<asp:TextBox ID="txtEmail" runat="server"></asp:TextBox>
</td>
</tr>[/html]

 

To reference this textbox control, Selenium out-of-the-box requires this:

[csharp]IWebElement e = driver.FindElement(By.XPath("//input[@id[substring(.,string-length()-7)=’txtEmail’]]"));[/csharp]

 

That’s not pretty. And you have to calculate the length of the controlId, take zero base into consideration (8 minus 1 = 7) and you will hopefully get a working reference. My preferred solution would be to add an extension to the By class, like By.RegEx() or something. But it’s a static class and extension methods require you to create an instance of it, so I had to scrap that idea (something for the next version of C#, Microsoft??)

My second best solution was a set of helper methods to generate the ugly XPath string. I’ve implemented a couple of options, where one get’s a reference without taking the type of control into consideration while the other allows you to look specifically for a textbox (input).

[csharp]public class SeleniumTools
{

public static string GetXPath(string controlId)
{
return GetXPath(controlId, "*");
}

public static string GetXPathForTextBox(string controlId)
{
return GetXPath(controlId, "input");
}

private static string GetXPath(string controlId, string controlType)
{
return "//" + controlType + "[@id[substring(.,string-length()-" + (controlId.Length – 1) + ")=’" + controlId + "’]]";
}
}
[/csharp]

 

At least this makes it a bit easier to read, and you don’t have to work out the XPath string manually:

[csharp]IWebElement e = driver.FindElement(By.XPath(SeleniumTools.GetXPath("txtEmail")));[/csharp]