ASP.NET Web Application Test Automation using the MUIA Library

The new Microsoft UI Automation (MUIA) library can automate both Windows applications and Web applications. However there is painfully little documentation on exactly how to perform test automation using MUIA. When writing Web application UI test automation, you first launch the Web app under test, then get references to the user controls of interest (such as text box controls), then manipulate the controls (add text, click buttons, etc.), and then examine the resulting state of the Web app’s controls to see if they match expected values to determine a pass/fail result. All these tasks are somewhat difficult but I usually have most trouble when trying to get references to the user controls. The image below comes from a little experiment program I wrote and summarizes a few of the key points about using the MUIA library to automate Web apps. The experiment program examines every AutomationElement (a MUIA concept) on a dummy Web app named MiniCalc. The program found a total of 80 elements. At the top level is the Internet Explorer browser itself, and it has a handle (a Windows concept) value of 280BD4. The IE browser has child Automation Elements, some of which are physical windows/controls (and therefore have handle values), but some child elements are just virtual AutomationElement objects (and there get a 0 window handle value). In the screenshot, notice that I used the Spy++ tool to examine the main client area in the browser (the turquoise colored body). The client area has handle value 1D1148. Now in the console app, notice that all the user controls are subchildren of the main client area, but they are not window controls (because they have no Window handle values). This is why using classical UI Windows control automation methods simply don’t work for Web applications — the user controls in a Web app are not Windows control windows. As a final observation, notice that the AutomationElement hierarchy for a Web application is moderately complex, and getting references to user controls is tricky because many user controls do not have Name properties, and there can be duplicate Name properties. I suspect that further investigation on my part will reveal that the most practical way to develop UI automation for Web applications is to make sure that developers assign to every user control, some property (like AccesssibleName perhaps) that can be accessed by MUIA to get an unambiguous reference to the control.
Here is the source code for the experiment program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation; // MUIA; add refs to UIAutomationClient.dll & UIAutomationTypes.dll
using System.Diagnostics; // for Process
namespace Run
  class Program
    static void Main(string[] args)
        Console.WriteLine("\nBegin MUIA control analysis");
        Process p = System.Diagnostics.Process.Start("iexplore.exe", "http://localhost/MiniCalc/Default.aspx");
        AutomationElement aeDesktop = AutomationElement.RootElement;
        AutomationElement aeBrowser = AutomationElement.FromHandle(p.MainWindowHandle);
        List<Info> list = StoreElements(aeBrowser);
        Console.WriteLine("List has " + list.Count + " Info objects\n");
        foreach (Info info in list)
          string s = " Name = " + info.Element.Current.Name + ", handle = " + info.Element.Current.NativeWindowHandle.ToString("X") + ", level = " + info.Level;
          Console.WriteLine(s.PadLeft(s.Length + info.Level * 2, ‘ ‘));
      catch (Exception ex)
        Console.WriteLine("Fatal: " + ex.Message);
    } // Main()
    class Info // utility class to add a Level to an AutomationElement
      public readonly AutomationElement Element;
      public readonly int Level;
      public Info(AutomationElement element, int level)
        this.Element = element; this.Level = level;
    } // class Info
    static List<Info> StoreElements(AutomationElement browserElement)
      List<Info> results = new List<Info>(); // final results. Info is an AutomationElement and a Level
      Stack<Info> stack = new Stack<Info>(); // helper stack of Info objects
      stack.Push(new Info(browserElement, 0)); // the browser is at level 0
      // do a preorder, non-recursive traversal
      while (stack.Count > 0)
        Info current = stack.Pop(); // get current AutomationElement info
        results.Add(current);       // add to the results List<>
        AutomationElementCollection subchildren = current.Element.FindAll(TreeScope.Children, Condition.TrueCondition);
        foreach (AutomationElement child in subchildren)
          stack.Push(new Info(child, current.Level + 1));
      return results;
    } // StoreElements
  } // class Program
} // ns Run   
This entry was posted in Software Test Automation. Bookmark the permalink.