"There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies and the other is to make it so complicated that there are no obvious deficiencies". C.A.R. Hoare
So you ask your self… “Where do I start”… I have been tasked with finding out how we can automate our WPF application for testing or just to drive development unit testing quicker. You have done some searches and stumbled upon this blog, but you have so many questions.
How does it work from a high level…The core concept is that we should be able to automate our application via a single API across different platforms (Winforms, WPF, ASP.NET). Microsoft has spent a great deal of time defining UI interaction patterns, so these can be abstracted out in a API that will allow us to drive the UI from code. This abstracted API is defined in patterns. There is a list of the patterns found for accessing controls:
http://msdn.microsoft.com/en-us/library/ms750574.aspx
and here is how to use the patterns to access properties on the element:
http://msdn.microsoft.com/en-us/library/ms752056.aspx
How do I drive the UI from a API standpoint…First you create some sort of driver application… like a console application. This application then uses code like this to create the process:
Process p = null;
p = Process.Start(@"C:\temp\UI.Shell.exe");
Once you have the process created you can pass the MainWindowHandle property of the process class to the Automation API. Another approach is to pass in the string found in the titlebar of the application and allow the automation API to search for the window. From there you use a variety of identifiers to reference the different controls. One approach is to use the AutomationID, which maps to the xaml controls x:name property. You can also use the ClassName to identify controls, which then maps to the .NET type of the control. If you are not sure of a controls AutomationID or ClassName you can use UISpy.exe (comes with the Windows SDK) to find out how you can reference the control.
Here is a code sample to give you an idea of how this all comes together. The following sample starts a WPF application and then completes a login view and triggers the login from a button.
Console.WriteLine("\nBegin WPF UIAutomation test run\n");
Console.WriteLine("Launching the application");
p = Process.Start(@"C:\temp\bin\wpfApplication.exe");
int ct = 0;
do
{
Console.WriteLine("Looking for application process. . . ");
++ct;
Thread.Sleep(10);
}
while (p == null && ct < 50);
if (p == null)
throw new Exception("Failed to find application process");
else
Console.WriteLine("Found application process");
Console.WriteLine("\nGetting Desktop");
AutomationElement aeDesktop = null;
aeDesktop = AutomationElement.RootElement;
if (aeDesktop == null)
throw new Exception("Unable to get Desktop");
Console.WriteLine("Found Desktop\n");
Console.WriteLine("\nLooking for application main window. . . ");
Thread.Sleep(5000);
AutomationElement application = AutomationElement.FromHandle(p.MainWindowHandle);
Console.WriteLine("\nCompleting controls. . . ");
AutomationElement loginView = application.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty,
"LoginView"));
AutomationElement loginPart = loginView.FindFirst(TreeScope.Children,
"LoginPart"));
AutomationElement txtUserName = loginPart.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.AutomationIdProperty,
"username"));
AutomationElement txtPassword = loginPart.FindFirst(TreeScope.Children,
"password"));
Console.WriteLine("\nSetting input to a030601");
ValuePattern vptxtUserName = (ValuePattern)txtUserName.GetCurrentPattern(ValuePattern.Pattern);
vptxtUserName.SetValue("testuser");
ValuePattern vptxtPassword = (ValuePattern)txtPassword.GetCurrentPattern(ValuePattern.Pattern);
vptxtPassword.SetValue("p@ssword1");
AutomationElement cmdLogin = loginPart.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.AutomationIdProperty, "authenticationButton"));
Console.WriteLine("\nClicking on Compute button");
InvokePattern ipLoginCmd =
(InvokePattern)cmdLogin.GetCurrentPattern(
InvokePattern.Pattern);
ipLoginCmd.Invoke();
Are there any good examples that show me how to drive the UI…There are a number of articles that I found… but the most simplistic and straight forward is an article that was published in MSDN magazine back in March of 2009. Automating UI Tests in WPF Applications.
Some more interesting information about WPF and automation for improving the quality of your application can be found in the WPF Application Quality Guide.
Posted in |Comments [0]
Sysknowlogy