WPF automation – locating and structure of WPF elements

Last Updated on by

Post summary: Guide how to locate WPF elements with Telerik Testing Framework.

References

This post is part of Automation of WPF applications with Telerik Testing Framework and TestStack White series. Sample application can be found in GitHub SampleApp repository.

MainWindow Page Object

MainWindow.cs class is representation of a main window of our sample application which is WPF. Note that code here is shortened. Full code can be found in GitHub repository.

using ArtOfTest.WebAii.Controls.Xaml.Wpf;
using ArtOfTest.WebAii.Silverlight;
using ArtOfTest.WebAii.TestTemplates;
using System.Threading;

namespace SampleApp.Tests.Framework.Elements
{
	public class MainWindow : XamlElementContainer
	{
		public static string WINDOW_NAME = "MainWindow";
		private string mainPath =
			"XamlPath=/Border[0]/AdornerDecorator[0]/ContentPresenter[0]/Grid[0]/";
		public MainWindow(VisualFind find) : base(find) { }

		private Button Button_Browse
		{
			get
			{
				return Get<Button>(mainPath + "Button[0]");
			}
		}

		public void ClickBrowseButton()
		{
			Button_Browse.User.Click();
			Thread.Sleep(500);
		}
	}
}

MainWindow is following Page Object design pattern. Elements are private Properties. Actions on elements are public methods that are the actual building blocks of the tests. Do not just add action methods for the sake of adding them. Add them if only the tests need such action.

If element is used in only one action then it could be inside the action method, but if element is used more than one it is mandatory to keep is as separate property. The main idea is to avoid duplications in order to improve maintainability. Remember to stay DRY: one element MUST be defined in only one place!

MainWindow class extends XamlElementContainer which comes from the framework. Constructor takes VisualFind object and passes it to XamlElementContainer’s constructor. This allows us to use framework’s methods for location of elements. In our case

public TControl Get<TControl>(params string[] clauses)
	where TControl : ArtOfTest.WebAii.Controls.Xaml.IFrameworkElement;

This is the strategy used by the Test Studio generated tests and I prefer it.

Different approach for MainWindow

Another element structure could be not to extend XamlElementContainer but pass VisualFind object through MainWindow’s constructor and use it internally to locate elements.

using ArtOfTest.WebAii.Controls.Xaml.Wpf;
using ArtOfTest.WebAii.Silverlight;
using System.Threading;

namespace SampleApp.Tests.Framework.Elements
{
	public class MainWindow
	{
		public static string WINDOW_NAME = "MainWindow";
		private string mainPath =
			"XamlPath=/Border[0]/AdornerDecorator[0]/ContentPresenter[0]/Grid[0]/";
		private VisualFind visualFind;
		public MainWindow(VisualFind find)
		{
			visualFind = find;
		}

		private Button Button_Browse
		{
			get
			{
				return visualFind.ByExpression(
					new XamlFindExpression(mainPath + "Button[0]")).
					CastAs<Button>();
			}
		}

		public void ClickBrowseButton()
		{
			Button_Browse.User.Click();
			Thread.Sleep(500);
		}
	}
}

How to locate elements is described in finding page elements article.

XamlPath

As you can see I’m using exact XamlPath find expression in order to locate my elements because I find it consistent and easy to maintain. XamlPath syntax is pretty similar to XPath. Difference is its indexes are zero based (Button[0] is the first element) and it doesn’t provide predicates and axes. The hard part is to get the XamlPath. I have found this lovely tool WPF Inspector. It gives a lot of information about structure of WPF application.

WPF Inspector

WPF Inspector

WPF Inspector

This is screen shot of the XamlPath for “Browse” button. In more complex UIs even with this great tool it is not that easy to get the XamlPath. If you find it hard you may consider other find expressions.

All elements on MainWindow are only WPF thus instance of White is not needed. In case of WinForms content hosted inside WPF you can pass instance of White through elements’ constructor and use it in actions inside.

public MainWindow(VisualFind find, Application applicationWhite) : base(find)
{
	appWhite = applicationWhite;
}

Having instance of White inside class you can work with it as explained in next post WPF automation – locating and structure of WPF elements.

If you find this post useful, please share it to reach more people. Sharing is caring!
Share on FacebookShare on LinkedInTweet about this on TwitterShare on Google+Email this to someone