Blog

Selenium syntax for CodedUI

16 Feb, 2016
Xebia Background Header Wave

I have been playing around with this idea for quite some time and I already build this for a customer in the past, but after questions I got from people using CodedUI who tried to come up with a simplified syntax I thought it would be time to publish what I have at the moment.

If you hand code your test automation with CodedUI, then you know that searching for a control is always a couple of lines of code to set up a search. You first create an instance of the control you need, provide it the search scope in the constructor and next you need to set the search properties. Once that is done you can then use the control and the moment you use one of it’s properties it will search for your control.

With Selenium this is a bit different. You have the Driver class that provides more or less to primary methods. FindElement and FindElements and you provide it an instance of the By class that contains the search criteria.

Let me illustrate what I mean with two examples. Bot do the same. We goto the google home page and type a search query.

CodedUI:

BrowserWindow b = BrowserWindow.Launch(new Uri("https://google.com"));
HtmlEdit searchBox = new HtmlEdit(b);
searchBox.SearchProperties.Add(HtmlEdit.PropertyNames.Id, "lst-ib");
Keyboard.SendKeys(searchBox, "codedUI Course{Enter}");

Selenium:

var driver = new ChromeDriver(@"driverfolder");
driver.Navigate().GoToUrl("https://google.com");
var searchBox  = driver.FindElement(By.Id("lst-ib"));
searchBox.SendKeys("codedUI Course{Enter}");

In general I hear that people like the Selenium syntax more then the multistep aproach in codedUI. The thing I dislike is that I lose the type safety of the type of control. So the question is, would it be possible to get the same syntax for codedUI and still use codedUI and get a type safe way of interacting with the search controls?

The awnser is (of course) yes this is possible by creating a couple of extension methods and a nifty implementation of the By class.

So let me first show you how this looks when you use the extension methods I created:

CodedUI with Selenium Syntax:

BrowserWindow b = BrowserWindow.Launch(new Uri("https://google.com"));
var searchBox= b.FindElement<HtmlEdit>(By.Id("lst-ib"));
searchBox.SendKeys( "codedUI Course{Enter}");

So now I have the type safety I am used to with codedUi but I have the more terse syntaxt for finding elements of a certain type.

So how does this extension method look like?
So first I created an extension method on UITestControl that looks like follows:

public static T FindElement<T>(this UITestControl container, Func<UITestControl,HtmlControl,HtmlControl>
 controlConstructorFunct) where T:HtmlControl , new()
{
   var control = new T {Container = container};
   controlConstructorFunct(container, control);
   return control ;
}

 

now the magic is in the fact that we pass this function a function that can initialize the control we just instantiated with the right search properties.

This is the implementation of the By class I just mentioned. It looks like follows:

public class By
{
  public static Func<UITestControl, HtmlControl,HtmlControl> Id(string id)
  {
    return (container,control) =>
    {
       control.SearchProperties.Add(HtmlControl.PropertyNames.Id, id);            return control;
    };
  }
}

 

Now the only thing we need to do is implement this on all the standard types of ways to find a control. So that would be by CSS class, By CSS query, By innerText, etc.

the final thing we need is an extension method on the UITestControl for handling mouse clicks and keyboard handling. those look like follows:

public static void Click(this UITestControl control)
{
  Mouse.Click(control);
}
public static void SendKeys(this UITestControl control, string text)
{
  Keyboard.SendKeys(control, text);
}

The final thing I did was also implement the way we do a search by css query a bit different. As I described in previous posts the way to find a control based on e.g. custom attributes as is the case with Angular sites, I use an implementation that calls a piece of Java script. This will then return the object and we need to ensure it is of the type we expect. Only restriction is that this is called on a UITestControl of the type BrowserWindow. Unfortunately I have not found a way to enforce this in the compiler, so I implemented that as a runtime check.

The code for this looks like follows:

public static Func<UITestControl, HtmlControl, HtmlControl>
                                     CssSelector(string cssSelectorToFind)
{
  const string javascript = "return document.querySelector('{0}');";
  var scriptToExecute = string.Format(javascript, cssSelectorToFind);
  return (container, control) =>
  {
    var browserWindow = container as BrowserWindow;
    if(browserWindow==null)
      throw new
              ArgumentException("You can only use the CSSSelector function on a control of type BrowserWindow");
   var searchControl =
            browserWindow.ExecuteScript(scriptToExecute) as HtmlControl;
   var foundControltype = searchControl?.GetType();
   var returnType = control.GetType();
   if (foundControltype?.FullName == returnType.FullName)
   {
      control = searchControl;
   }
   else
   {
      throw new InvalidCastException(
     $"Unable to assign control found to type {returnType.FullName}, control is of type {foundControltype?.FullName}");
   }
   return control;
  };
}

I put the final code here on GitHub, so you can easily download and contribute if you have additional insights.

What I shared is not complete, but I rather share it now then wait for me to find the time to finish it to support the whole Selenium set of methods.

Let me know what you think.

Hope this helps!

Marcel de Vries
Marcel is a key figure in the technology sector, serving as the co-founder and Global MD & CTO of Xebia Microsoft Services. He is deeply involved in advancing organizational capabilities to deploy software swiftly and securely, emphasizing the importance of innovation and productivity while maintaining compliance. Marcel is passionate about technology and continuous learning, often sharing his insights at leading industry events and through online courses on Pluralsight. Recognized for his contributions, Marcel has been honored with the Microsoft MVP award for over 17 consecutive years and is a Microsoft Regional Director since 2008. His expertise spans across Cloud Adoption Strategies, DevOps, and various cloud computing frameworks, making him a respected voice in the tech community.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts