Automated Testing of WPF application which has custom controls
Recently I have been involved in testing of a windows application developed in WPF, the project has been on-going for several years with not much automated testing.
I was keen on automating so started evaluating automating tools and to my surprise none of the standard tools would recognise any controls in this application.
We had to use their custom built development framework to build the application .As its custom built we started researching on how to automate and we concluded that ‘Automation Peer’ classes needs to be implemented to make the application automatable. So we then implemented AutomationPeer classes which exposed the custom controls to automated testing.
This has enabled commercial test tools to recognise some of the controls in application however there is a library UI Automation(UIA) also called as ‘Windows Automation API’ for automated testing provided by Microsoft worked with most of the controls so we decided to use that for our automated testing.
UIA can detect controls which has automation peers implemented and can work like Selenium,this library is available for free for almost all versions of windows operating systems.
To start automating I first need to add this library to the project in visual studio.
Here I have a calculator built with WPF, in the calculator I'm attempting to get the properties of the text box
I'm aiming to enter something into this text box for that I first need to get the properties of the parent window and then I need to get the properties of the text box
Using ‘UI Spy’I got the details about a control by placing the mouse cursor on the text box and pressing ‘Ctrl’ in the keyboard UI Spy displays the properties of the text box
If I click parent control of the textbox (“window” “MyCalculator v1”)on the above screenshot I can get the details of the parent window.
Every test object is an automation element in UIA which is similar to a web element in Selenium. To enter something into the textbox I have to recognise the Calculator window first so here is my code to do so
AutomationElement _calcwindow = AutomationElement.RootElement.FindFirst(TreeScope.Children,
newPropertyCondition(AutomationElement.NameProperty, "MyCalculator v1"));
Then I got the object reference for the text box
AutomationElement textbox = _calcwindow.FindFirst(TreeScope.Children,
newPropertyCondition(AutomationElement.AutomationIdProperty, "tb"));
now to enter value into the text box I have written a helper function in a Helper Static Class
publicstaticvoidEnterValueIntoTextBox(thisAutomationElementtextBox , stringval)
{
objectvaluePattern = null;
varval = textBox.TryGetCurrentPattern(ValuePattern.Pattern, outvaluePattern);
if (textBox.TryGetCurrentPattern(
ValuePattern.Pattern, outvaluePattern))
{
// Set focus for input functionality and begin.
textBox.SetFocus();
// Pause before sending keyboard input.
Thread.Sleep(100);
((ValuePattern)valuePattern).SetValue(val);
}
}
The above does seem like a lot of code compared to Selenium or Coded UI box but once the helper functions are written it can be reused for every textbox in the application.The finished code looks like below
usingSystem.Windows.Automation;
usingNUnit.Framework;
namespaceMyCalculator
{
[TestFixture]
publicclassCalcTest
{
[Test]
publicvoidcalculatorTest()
{
#regionTestObjects
AutomationElement _calcwindow = AutomationElement.RootElement.FindFirst(TreeScope.Children,
newPropertyCondition(AutomationElement.NameProperty, "MyCalculator v1"));
AutomationElement textbox = _calcwindow.FindFirst(TreeScope.Children,
newPropertyCondition(AutomationElement.AutomationIdProperty, "tb"));
#endregion
textbox.EnterValueIntoTextBox("1");
}
}
}
usingSystem.Threading;
usingSystem.Windows.Automation;
namespaceMyCalculator
{
publicstaticclassHelper
{
publicstaticvoidEnterValueIntoTextBox(thisAutomationElementtextBox, string value)
{
objectvaluePattern = null;
if (textBox.TryGetCurrentPattern(
ValuePattern.Pattern, outvaluePattern))
{
// Set focus for input functionality and begin.
textBox.SetFocus();
// Pause before sending keyboard input.
Thread.Sleep(100);
((ValuePattern)valuePattern).SetValue(value);
}
}
}
}
I have used nunit for the above sample to run the tests.
The question now is how easy is it for QA’s to use compared to CodedUI,Ranorex or other commercial tools? It may not be as easy as using the commercial tools as they have built in functions to enter the text.
textbox.Text = “value” will do the job for you as opposed to the helper function above but I can recommend this if you have a Technical QA and if you are on a budget.