Category Archives: UI Testing

TestStack.White Gotcha/Tips

RadioButton Click might not actually change anything

The click method does not actually click on the radio button itself. It’s noticeable where a radio button fills some extra space, in some cases the click will not be over the radio button or the text and thus doesn’t seem to work.

Instead use

var radioButton = window.Get<RadioButton>(SearchCriteria.ByText("One"));
radioButton.SetValue(true);

Assert.IsTrue(radioButton.IsSelected);

What type is a UserControl mapped to in TestStack.White?

WPF UserControl’s maps to the TestStack.White frameworks CustomUIItem. Hence

<UserControl 
   x:Class="MyClass"
   x:Name="myClass">
<!-- Other elements -->
</UserControl>

can be accessed using

var myClassUserControl =
   window.Get<CustomUIItem>(
      SearchCriteria.ByAutomationId("myClass"));

Defining a custom control mapping

When using the generic Get method in TestStack.White, you’re have the ability to convert the automation control to a TestStack.White Label, Button etc. to give the feel of interacting with such capabilities that are exposed by these types of controls.

In the case of a WPF UserControl we see this maps to a CustomUIItem. It might be useful if we were to define a TestStack.White compatible UserControl for use with the Get method (for example).

Let’s firstly look at how TestStack.White source code implements a Label (here’s the source for the Label control)

public class Label : UIItem
{
   protected Label() {}
   public Label(AutomationElement automationElement, 
       IActionListener actionListener) : 
          base(automationElement, actionListener) {}

   public virtual string Text
   {
      get { return (string) Property(AutomationElement.NameProperty); }
   }
}

Now in our case we need to create a similar class but derived from the CustomUIItem, so here’s ours

[ControlTypeMapping(CustomUIItemType.Custom, WindowsFramework.Wpf)]
public class UserControl : CustomUIItem
{
   public UserControl(
      AutomationElement automationElement, 
      ActionListener actionListener)
         : base(automationElement, actionListener)
   {            
   }

   protected UserControl()
   {            
   }
}

According to the Custom UI Items documentation, an Empty constructor is mandatory with protected or public access modifier also required.

The ControlTypeMapping attribute is used to allow TestStack.White to map the return from the Get method to the new UserControl type, for example

var userControl = window.Get<UserControl>(
   SearchCriteria.ByAutomationId("myClass"));

Selecting an item in a ComboBox

The code for selecting an item in a ComboBox is fairly simple in TestStack.White, but when I used it I kept getting exceptions saying something about virtualization pattern.

Luckily as TestStack.White is built upon the MS Automation framework and others have been here before me, this from Stackoverflow worked for me, here’s the code slightly altered to use as an extension method

public static void SelectItem(this ComboBox control, string item)
{
   var listControl = control.AutomationElement;

   var automationPatternFromElement = 
      GetSpecifiedPattern(listControl,
         "ExpandCollapsePatternIdentifiers.Pattern");

   var expandCollapsePattern =
      listControl.GetCurrentPattern(automationPatternFromElement) 
         as ExpandCollapsePattern;
   
   if(expandCollapsePattern != null)
   {
      expandCollapsePattern.Expand();
      expandCollapsePattern.Collapse();

      var listItem = listControl.FindFirst(
          TreeScope.Subtree,
          new PropertyCondition(AutomationElement.NameProperty, item));

      automationPatternFromElement = 
         GetSpecifiedPattern(listItem, 
            "SelectionItemPatternIdentifiers.Pattern");

      var selectionItemPattern =
         listItem.GetCurrentPattern(automationPatternFromElement) 
            as SelectionItemPattern;

      if(selectionItemPattern != null)
      {
         selectionItemPattern.Select();
      }
   }
}

private static AutomationPattern GetSpecifiedPattern(
   AutomationElement element, string patternName)
{
   return element.GetSupportedPatterns()
      .FirstOrDefault(pattern => 
         pattern.ProgrammaticName == patternName);
}

UI Automation Testing with TestStack.White

TestStack.White is based on the UI Automation libraries (see UI Automation), offering a simplification of such methods for automating a UI and allowing us to write unit tests against such UI automation.

Getting Started

Let’s jump straight in and write a simply UI automation unit test around the Calc.exe application.

  • Create a new C# Unit Test project (or class library, adding your favoured unit testing framework)
  • Install the TestStack.White nuget package

Let’s begin by creating a simple test method which starts the Calc.exe application, get’s access to the calculator window and then disposes of it, we’ll obviously insert code into this test to do something of value soon, but for now, here’s the basics

[TestMethod]
public void TestMethod1()
{
   using(var application = Application.Launch("Calc.exe"))
   {
      var calculator = application.GetWindow("Calculator", InitializeOption.NoCache);

      // do something with the application

      application.Close();
   }
}

Well that doesn’t do anything too exciting, it runs Calc.exe and then closes it, but now we can start interacting with an instance of the calculator’s UI using TestStack.White.

Let’s start by getting the button with the number 7 and click/press it.

var b7 = calculator.Get<Button>(SearchCriteria.ByText("7"));
b7.Click();

By using the Get method with the generic parameter Button, we get back a button object which we can interact directly with. The SearchCriteria allows us to try to find UI control in the Calculator with the text (in this case) 7. As is probably quite obvious, we call the Click method on this button object to simulate a button click event.

We can’t always get as controls by their text so using Spy++ and using the cross-hair/find window tool we can find the “Control ID” (which is in hex.) and we can instead find a control via this id (White calls this the automation id) hence

var plus = calculator.Get<Button>(
       SearchCriteria.ByAutomationId(
           0x5D.ToString()));
plus.Click();

So let’s look at a completed and very simply unit test to see that we can add two numbers and the output (on the screen) is expected

var b7 = calculator.Get<Button>(
   SearchCriteria.ByText("7"));
b7.Click();

var plus = calculator.Get<Button>(
   SearchCriteria.ByAutomationId(
      0x5D.ToString()));
plus.Click();

var b3 = calculator.Get<Button>(
   SearchCriteria.ByText("5"));
b3.Click();

var eq = calculator.Get<Button>(
   SearchCriteria.ByAutomationId(
      0x79.ToString()));
eq.Click();

var a = calculator.Get(
   SearchCriteria.ByAutomationId(
      0x96.ToString()));

var r = a.Name;
Assert.AreEqual("12", r);

Managed applications

In the above example we uses Spy++ to get control id’s etc. for WPF we can use the utility, Snoop and for the automation id use the name of the control, for example

var searchBox = pf.Get<TextBox>(
   SearchCriteria.ByAutomationId("SearchBox"));

where SearchBox is the name associated with the control.

References

http://teststackwhite.readthedocs.io/en/latest/
https://github.com/TestStack/White