December 2013

Volume 28 Number 12

Visual Studio 2013 - Cross-Browser, Coded UI Testing with Visual Studio 2013

By Damian Zapart

Web-based solutions have become popular in the past few years because they provide easy access to users all around the world. Users also like them because of their convenience. Users don’t need to install a separate application; with browsers alone they can connect to their accounts from any device connected to the Internet. However, for both software developers and testers, the fact that a user can choose any Web browser presents a problem—you must test a solution against multiple browsers. In this article, I’ll demonstrate how this problem might be resolved in a simple way by creating coded UI test cases that will execute against any modern browser, using only C#.

The New Visual Studio

A few years ago, when Visual Studio 2010 was released, one of its most interesting features was the ability to test the UI of Web-based solutions. However, at the time, there were some limitations of using this technology; for example, the only Web browser supported was Internet Explorer. Moreover, testing the UI relied on recording user actions on the Web site and then replaying them to simulate real user actions, which many developers found unacceptable.

The new Visual Studio 2013, available as a release candidate (RC), brings a host of improvements in many different areas, ranging from new IDE features to an extended test framework (a long list of changes in the RC version is available at bit.ly/1bBryTZ). From my perspective, two new features are particularly interesting. First, you can now test the UI of not only Internet Explorer (including Internet Explorer 11) but also all other modern browsers, such as Google Chrome and Mozilla Firefox. Second, and even more crucial from a test development point of view, is what Microsoft calls “configurable properties for coded UI tests on the browser.” Basically, this new functionality defines a set of search criteria for UI elements. I’ll describe these features in more detail later in this article.

System Under Test

Those two new features are what I’ll use to create cross-browser, fully coded UI tests. For my system under test (SUT), I want a public, well-known, Web-based application, so I chose Facebook. I want to cover two basic user scenarios. The first scenario is a positive test case that will display a profile page after successful login. The second is a negative test case in which I enter invalid user credentials and try to log in. In this case, I expect some error message in the user response.

There are a few challenges I’ll need to resolve. First, the correct browser needs to be launched (based on the test configuration), and it must be able to provide access to a specific URL. Second, during run time, a specific control element must be extracted from the HTML document to provide input for the simulated user. Wherever needed, values must be entered for the control and the correct buttons clicked to submit an HTML form to the server. The code should also be able to handle a response from the server, validate it and, finally, close the browser when the test cases are finished (in the test’s cleanup method).

Before Coding

Before I start coding I need to prepare my environment, which is pretty straightforward. First I need to download Visual Studio 2013 RC from bit.ly/137Sg3U. By default, Visual Studio 2013 RC lets you create coded UI tests just for Internet Explorer, but that’s not what I’m interested in; I want to create tests for all modern browsers. Of course, there will be no compilation errors as long as I indicate in my code that the tests should run against browsers other than Internet Explorer, but an unhandled exception will be thrown during run time. Later on I’ll show how to change the browser as well. To avoid problems during coding, I need to download and install a Visual Studio extension called “Selenium components for Coded UI Cross-Browser Testing” (bit.ly/Sd7Pgw), which will let me execute tests on any browser installed on my machine.

Jump into the Code

With everything in place I can now demonstrate how to create a new Coded UI Project. Open Visual Studio 2013 and click on File | New Project | Templates | Visual C# | Tests | Coded UI Test Project. Enter the project name and press OK to see the new solution, as shown in Figure 1.

Creating a New Coded UI Test Project
Figure 1 Creating a New Coded UI Test Project

The solution is essentially divided into three strongly connected groups, as shown in Figure 2. The first group contains a Pages namespace, which includes the BasePage class, from which ProfilePage and LoginPage derive. These classes expose properties and logic for operating on the page currently displayed in the browser. Such an approach helps split the test case implementations from browser-specific operations such as searching for a control by Id. The test cases operate directly on the properties and functions exposed by the page classes.

Coded UI Test Solution Diagram
Figure 2 Coded UI Test Solution Diagram

In the second group I put all extensions (UIControlExtensions), selectors (SearchSelector) and browser-related common classes (BrowserFactory, Browser). This subset of objects stores the implementation logic of the HTML elements search engine (which I’ll describe shortly). I also added my own browser-related objects, which help in running test cases against the correct Web browser. The last group contains the test file (FacebookUITests) with the implementations of the test cases. Those test cases never operate on the browser directly; instead, they use the panel classes.

An important part of my project is the HTML controls search engine, so my first step is to create a static class called UIControl­Extensions, which contains the implementation logic for finding and extracting specific controls from the currently opened Web browser page. To make the coding easier—and to be able to reuse it later in the project—I don’t want to have to initialize instances of this class every time I need to use it, so I’m going to implement it as an extension method that will extend the built-in UITestControl type. Additionally, any extension functions I implement will be generic. They must derive from HtmlControl (the base class for all UI controls in the Coded UI Test Framework) and must contain a default parameter-less constructor. I want this function to be generic because I intend to search only for specific control types (see the list of available HTML types at bit.ly/1aiB5eW).

I’ll pass search criteria to my function via the selectorDefinition parameter, of SearchSelector type. The SearchSelector class is a simple type, but it’s still very useful. It exposes a number of properties, such as ID or Class, which can be set from another function and later be transformed, using reflection, to the PropertyExpressionCollection class (bit.ly/18lvmnd). Next, that property collection can be used as a filter to extract just the small subset of HTML controls that match the given criteria. Later, the generated property collection is assigned to the SearchProperties property (bit.ly/16C20iS) of the generic object, which lets it call the Exists property and FindMatchingControls function. Keep in mind that Coded UI Test Framework algorithms won’t search for specific controls on the whole page by default, and will process all children and sub-­children only on the extended UITestControl. This helps improve the performance of the test cases because search criteria can be applied to just a small subset of the HTML document; for example, to all children of some DIV container, as shown in Figure 3.

Figure 3 Searching for HTML Controls

private static ReadOnlyCollection<T> 
  FindAll<T>(this UITestControl control, 
  SearchSelector selectorDefinition) where T : HtmlControl, new()
{
  var result = new List<T>();
    T selectorElement = new T { Container = control };
      selectorElement.SearchProperties.AddRange(
        selectorDefinition.ToPropertyCollection());
    if (!selectorElement.Exists)
      {
        Trace.WriteLine(string.Format(
          "Html {0} element doesn't exist for given selector {1}.",
             typeof(T).Name, selectorDefinition),"UI CodedTest");
          return result.AsReadOnly();
      }
    return selectorElement
        .FindMatchingControls()
        .Select(c => (T)c).ToList().AsReadOnly();
      }
}

I’ve implemented the core of the search control engine, but the function FindAll<T> requires a lot of code and knowledge to get it working properly—you have to specify the search parameters, check whether an item exists, and so on. That’s why I decided to make it private and expose two other functions instead:

public static T FindById<T>(this UITestControl control, 
  string controlId) where T : HtmlControl, new()
public static T FindFirstByCssClass<T>(this UITestControl control, 
  string className, bool contains = true) where T : HtmlControl, new()

These generic methods are much more useful because they’re “single purpose” and reduce varying dimensions of expected input to simple types. Under the hood, both functions call the FindAll<T> function and operate on its result, but the implementation is hidden inside their bodies.

Working with Any Browser

I’ve already put some effort into finding and retrieving controls, but to test whether my functions are implemented correctly I need to get a Web browser working, which means it needs to be launched. Launching a particular browser is as easy as any other browser-related operation. As I mentioned earlier, I want to place all browser-related operations inside page-related classes. However, launching the browser is not a part of testing—it’s a prerequisite. Motivated by software development best practices, I decided to create a BasePage class, shown in Figure 4, to store common operations for all derived page classes (including launching a browser) without any redundancy.

Figure 4 The BasePage Class

public abstract class BasePage : UITestControl
{
  protected const string BaseURL = "https://www.facebook.com/";
  /// <summary>
  /// Gets URL address of the current page.
  /// </summary>
  public Uri PageUrl{get; protected set;}
  /// <summary>
  /// Store the root control for the page.
  /// </summary>
  protected UITestControl Body;
  /// <summary>
  /// Gets current browser window.
  /// </summary>
  protected BrowserWindow BrowserWindow { get; set; }
  /// <summary>
  /// Default constructor.
  /// </summary>
  public BasePage()
  {
    this.ConstructUrl();
  }
  /// <summary>
  /// Builds derived page URL based on the BaseURL and specific page URL.
  /// </summary>
  /// <returns>A specific URL for the page.</returns>
  protected abstract Uri ConstructUrl();
  /// <summary>
  /// Verifies derived page is displayed correctly.
  /// </summary>
  /// <returns>True if validation conditions passed.</returns>
  public abstract bool IsValidPageDisplayed();
}

The static, generic Launch<T> function is also a part of the Base­Page class. Inside the function body, a new instance of the specific page type (derived from BasePage) is initialized based on the parameter-­less default constructor. Later in the code, the target Web browser is set based on the value of the browser parameter (“ie” for Internet Explorer, “chrome” for Google Chrome and so on). This assignment specifies the browser against which the current test will be executed. The next step is to navigate to some URL in the selected browser. This is handled by BrowserWindow.Launch(page.ConstructUrl()), where the ConstructUrl function is a specific function for each derived page. After launching the browser window and navigating to the specific URL, I store the HTML body inside the BasePage property and optionally maximize the browser window (this is desirable because the page controls might overlap and automated UI actions could fail). I then clear the cookies, because each test should be independent. Finally, in the Launch function shown in Figure 5, I want to check whether the currently displayed page is the correct one, so I call IsValidPageDisplayed, which will execute in the context of the generic page. This function finds all required HTML controls (login, password, submit button) and validates they exist on the page.

Figure 5 The Launch Function

public static T Launch<T>(
  Browser browser = Browser.IE,
  bool clearCookies = true,
  bool maximized = true)
  where T : BasePage, new()
{
  T page = new T();
  var url = page.PageUrl;
  if (url == null)
  {
    throw new InvalidOperationException("Unable to find URL for requested page.");
  }
  var pathToBrowserExe = FacebookCodedUITestProject
        .BrowserFactory.GetBrowserExePath(browser);
  // Setting the currect browser for the test.
  BrowserWindow.CurrentBrowser = GetBrowser(browser);
  var window = BrowserWindow.Launch(page.ConstructUrl());
  page.BrowserWindow = window;
  if (window == null)
  {
    var errorMessage = string.Format(
        "Unable to run browser under the path: {0}", pathToBrowserExe);
          throw new InvalidOperationException(errorMessage);
            }
            page.Body = (window.CurrentDocumentWindow.GetChildren()[0] as
                          UITestControl) as HtmlControl;
  if (clearCookies)
  {
  BrowserWindow.ClearCookies();
  }
  window.Maximized = maximized;
  if (!page.IsValidPageDisplayed())
  {
    throw new InvalidOperationException("Invalid page is displayed.");
  }
  return page;
}
 }

Web browsers evolve continuously and you may not realize when this happens. Sometimes this means certain features aren’t available in the new browser version, which in turn causes some tests to fail, even if they were passed previously. That’s why it’s important to disable automatic browser updates and wait until the new version is supported by the Selenium components for Coded UI Cross-Browser Testing. Otherwise, unexpected exceptions might occur during run time, as shown in Figure 6.

An Exception After a Web Browser Update
Figure 6 An Exception After a Web Browser Update

Testing, Testing, Testing

Finally, I’ll write some logic for my tests. As I mentioned earlier, I want to test two basic user scenarios. The first is a positive login process (a second negative test case is available in the project source code, which you can find at msdn.com/magazine/msdnmag1213). To get this test running, I must create a specific page class that derives from the BasePage, as shown in Figure 7. Inside my new class, in private fields, I place all the constant values (controls, Ids and CSS class names) and create dedicated methods that use those constants to extract specific UI elements from the current page. I also create a function called TypeCredentialAndClickLogin(string login, string password) that fully encapsulates the login operation. At run time, it finds all required controls, simulates the typing in of values passed as parameters, and then presses the Login button by clicking the left mouse button.

Figure 7 The Login Page

public class LoginPage : BasePage
  {
    private const string LoginButtonId = "u_0_1";
    private const string LoginTextBoxId = "email";
    private const string PasswordTextBoxId = "pass";
    private const string LoginFormId = "loginform";
    private const string ErrorMessageDivClass = "login_error_box";
    private const string Page = "login.php";
    /// <summary>
    /// Builds URL for the page.
    /// </summary>
    /// <returns>Uri of the specific page.</returns>
    protected override Uri ConstructUrl()
    {
      this.PageUrl = new Uri(string.Format("{0}/{1}", BasePage.BaseURL,
         LoginPage.Page));
      return this.PageUrl;
    }
    /// <summary>
    /// Validate that the correct page is displayed.
    /// </summary>
    public override bool IsValidPageDisplayed()
    {
      return this.Body.FindById<HtmlDiv>(LoginTextBoxId) != null;
    }
    /// <summary>
    /// Gets the login button from the page.
    /// </summary>
    public HtmlInputButton LoginButton
    {
      get
      {
        return this.Body.FindById<HtmlInputButton>(LoginButtonId);
      }
    }
    /// <summary>
    /// Gets the login textbox from the page.
    /// </summary>
    public HtmlEdit LoginTextBox
    {
      get
      {
        return this.Body.FindById<HtmlEdit>(LoginTextBoxId);
      }
    }
    /// <summary>
    /// Gets the password textbox from the page.
    /// </summary>
    public HtmlEdit PasswordTextBox
    {
      get
      {
        return this.Body.FindById<HtmlEdit>(PasswordTextBoxId);
      }
    }
    /// <summary>
    /// Gets the error dialog window - when login failed.
    /// </summary>
    public HtmlControl ErrorDialog
    {
      get
      {
        return this.Body.FindFirstByCssClass<HtmlControl>("*login_error_box ");
      }
    }
    /// <summary>
    /// Types login and password into input fields and clicks the Login button.
    /// </summary>
    public void TypeCredentialAndClickLogin(string login, string password)
    {
      var loginButton = this.LoginButton;
      var emailInput = this.LoginTextBox;
      var passwordInput = this.PasswordTextBox;
      emailInput.TypeText(login);
      passwordInput.TypeText(password);
      Mouse.Click(loginButton, System.Windows.Forms.MouseButtons.Left);
    }
  }

After I create the required components, I can build a test case. This test function will validate that the login operation completes successfully. At the beginning of the test case, I launch the Login page using the Launch<T> static function. I pass all required values into the login and password input fields, then click the Login button. When the operation finishes, I validate that the newly displayed panel is a Profile page:

[TestMethod]
public void FacebookValidLogin()
{
  var loginPage = BasePage.Launch<LoginPage>();
  loginPage.TypeCredentialAndClickLogin(fbLogin, fbPassword);
  var profilePage = loginPage.InitializePage<ProfilePage>();
  Assert.IsTrue(profilePage.IsValidPageDisplayed(),
     "Profile page is not displayed.");
}

While searching for a control with a specific CSS class, I noticed that in the Coded UI Test Framework a complication might arise. In HTML, controls can have more than one class name in the class attribute, and this of course affects the framework I’m working with. If, for example, my current Web site contains a DIV element with attribute class “A B C” and I use the SearchSelector.Class property to find all controls with CSS class “B,” I might not get any result—because “A B C” is not equal to “B.” To deal with this, I introduce the star “*” notation, which changes class expectation from “equals” to “contains.” So, to get this example working, I need to change  class “B” to class “*B.”

What If …

Sometimes tests fail and you have to ask why. In many cases, reviewing the test log is all that’s needed to answer this question—but not always. In the Coded UI Test Framework, a new feature provides extra information on demand.

Assume the test failed because a page different than expected was displayed. In the logs, I see that some required control was not found. This is good information, but it doesn’t give the full answer. With the new feature, however, I can capture the currently displayed screen. To use that feature, I just have to add the capture and a way to save it in the test cleanup method, as shown in Figure 8. Now I get detailed information for any test that fails.

Figure 8 The Test Cleanup Method

[TestCleanup]
public void TestCleanup()
{
  if (this.TestContext.CurrentTestOutcome != null &&
     this.TestContext.CurrentTestOutcome.ToString() == "Failed")
{
    try
    {
      var img = BrowserWindow.Desktop.CaptureImage();
      var pathToSave = System.IO.Path.Combine(
        this.TestContext.TestResultsDirectory,
        string.Format("{0}.jpg", this.TestContext.TestName));
      var bitmap = new Bitmap(img);
      bitmap.Save(pathToSave);
    }
    catch
    {
      this.TestContext.WriteLine("Unable to capture or save screen.");
    }
  }
}

Wrapping Up

In this article I showed how quick and easy it is to start using the new Coded UI Test Framework in Visual Studio 2013 RC. Of course, I described only the basic use of this technology, including managing different browsers, and supporting a variety of operations to find, retrieve and manipulate HTML controls. There are many more great features worth exploring.


Damian Zapart is a .NET developer with more than seven years of experience. He focuses mainly on Web-based technologies. He’s a programming geek interested in cutting-edge technologies, design patterns and testing. Visit his blog at bit.ly/18BV7Qx to read more about him.

Thanks to the following Microsoft technical experts for reviewing this article: Bilal A. Durrani and Evren Önem
Bilal A. Durrani (Microsoft) is a test engineer at Microsoft in the OTTS Europe team. His job is to assure the quality of Cloud based TV services and HTML5 clients that effect millions of users.

Evren Onem (Microsoft) is a developer at Microsoft in the OTTS Europe team. His job is to design and build Cloud-based live TV services for millions of end users. Amongst his recent technical interests are JavaScript and NodeJS-based server-side development. He also is a night-time researcher on cognitive radio ad hoc networks.