UIAutomation, Coded UI tests, AutomationPeer and WPF/silverlight custom controls
The Coded UI Tests, available in Visual Studio Ultimate or Premium, enable the creation of automated tests for the User Interface. This is a really nice feature because you are no more forced to make "hand made" tests which takes hours to be performed.
The WPF controls in the framework are ready to be used by the Microsoft UI Automation which is itself used by the coded UI tests. This means that when you use the screen recorder to record the tests on your UI, it will be able to find the several control used in your application.
When you create you own custom controls or extend standard one, the recorder would not be able to find them at first and so a whole part of the screen may not be available for tests. Actually, it is possible to record a test but every steps will be done using screen position: click at (120,30), drag from (120,30) to (10,40). This is really annoying because any changes in the UI may broke all your tests.
In this post, we will see how to make a custom control fully useable in Coded UI tests scenarii. We will so answer the question "Why cannot the code UI test recorder find anything inside my WPF or Silverlight custom control ?"
Note: the same technique is used by the accessibility clients and by enabling this feature you also ease the people using your application through UI automation client like the partially-sighted person.
How it works
Everything is based on a class named AutomationPeer. When an UI Automation client analyzes your User Interface, it looks for an automation peer and uses it to walks trough the tree of peers. The tree of peers is nearly a visual tree but it exposes only the relevant part of the interface.
Every standard controls have a regarding automation peer already defined. But if you add new parts on them or if you built from scratch a new control, you have to provide a related automation peer.
If not the automation peer will not be aware of this parts and they won’t be available to the automation clients. Also if a standard control is used but the “normal” visual tree is broken, for example by splitting the normal items in several parts, it is necessary to expose the news parts. I’ve seen this case in some controls where the content of the standard content presenter where taken apart to be place in another place of the Visual Tree. As you will see, this is done in the GetChildrenCore method of the new AutomationPeer.
How to make your controls available to the UI Automation client
The AutomationPeer are created and retrieved for each control in the method named OnCreateAutomationPeer. The custom control must overrides this method to provide an AutomationPeer related to its specific implementation.
If the control is build from scratch and its base class is Control, there is no built-in automation peer because there is simply none at all for the Control class. However, the FrameworkElementAutomationPeer class can be used as a base class.
If the controls inherits from an another standard one like TabControl, it’s possible to inherit from its regarding AutomationPeer and customize its behavior. This is especially needed if new controls are added to the template of the control because the basic AutomationPeer(let’s say TabControlAutomationPeer) does not know about them. By convention, the automation peer classes name starts with the name of the related control and end with AutomationPeer.
There is several methods in the AutomationPeer that can be overridden to make it works the right way. The embolden methods are the one which are required at least:
- GetClassNameCore: it returns the name of the class the automationpeer is builded for.
- GetAutomationControlTypeCore : returns an enum telling which behavior implements the automated control. For example it can be Button, Tab, Window, etc.
- GetPattern: tells which kind of functional behavior is fulfilled by the control: IRangeValueprovider, ITextProvider, IValueProvider, etc.
- GetChildrenCore: this methods returns a list of automationPeer which are related to the children of your control. If you add a new visual part to a standard control, this is where you must add the new child.
- IsControlElementCore and IsContentElementCore : tells the UI Automation client which type of automated control it is : for reading or for interactive purpose. These can improve the performance when being used as a filter.
Also, you can use the static method CreatePeerForElement of the UIElementAutomationPeer class to create an AutomationPeer of a know control. This is especially helpful when new control have to be added.
Finally, the UI Automation clients do not follows the changes in the UI and they have to be noticed manually. This is done via the RaisePropertyChanged on the relative automation peer and have to be done on every change. This is very familiar from what it is necessary to do in the INotifyPropertyChanged to notify the binding of a change.
Here is an example of an AutomationPeer for the HeaderedControl in the Amazing wpf control library.
And here is an example of code you can implement to raise the property changed event on a peer :
1: if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
- UI Automation of a custom control on MSDN
- Testing the user interface with coded UI Tests on MSDN
- Amazing control library on Codeplex
Comments are wide open if you have any question !