This blog assumes that you have a prior understanding of Coded UI Test extensibility. If not, a good starting point would be here. Also check out additional links in the “Extending Coded UI Test” section available here.
In this post, I’ll enumerate a few points you would need to watch out for while implementing an extension plugin. Some of the data are a compilation of actual issues the plugin writers hit while developing their plugin in collaboration with the Coded UI Test team.
1. Set the ComVisibleAttribute
Ensure that the System.Runtime.InteropServices.ComVisibleAttribute is set to true for both technology manager and technology element class. For example,
[ComVisible(true)]
public class TelerikGridViewTechnologyElement : UITechnologyElement { … }
[ComVisible(true)]
public sealed class TelerikGridViewTechnologyManager : UITechnologyManager { … }
2. Implement some caching mechanism for properties in property provider.
You could be using different approaches for fetching the control properties. It could be through an accessibility interface or some code injection technique with some remoting mechanism to communicate between the two processes. Either way, always ensure that you property fetching is optimized, specifically for properties whose value remain constant throughout.
A good example is ControlType. This property is accessed a lot number of times on each PropertyProvider call at different contexts. Ensure that such property values for the controls are cached in your plugin. Secondly, use lazy initialization for the property values. Use your own diligence to decide on whether to do bulk fetching versus individual property fetching. At instances we have observed as much as 3x improvement in performance with proper level of caching implemented.
3. Limit the hierarchy for the specialized class
Ensure you provide the correct level of ancestor in the hierarchy. For example, the Desktop UITestControl need not be specified as the container for the TopLevelWindow. It simply creates an unnecessary hierarchy during CodeGeneration.
public WpfInjectedWindow() : base(UITestControl.Desktop) { … }
Instead, simplify it as
public WpfInjectedWindow() : base(null) { … }
4. Do not access “live” UITestControl properties in UITestPropertyProvider.GetControlSupportLevel(UITestControl)
No new property fetching should be done within the GetControlSupportLevel() method. The UITestControl passed to this method may not necessarily be a “live” control i.e. may not exist currently in the desktop session. (Note: The GetControlSupportLevel() is called both in the context of Playback as well as CodeGeneration). So the decision of support level should be based solely on the static information available in the UIObject map i.e. search properties, technology name, etc.
Here is an example that makes use of ClassName and AccessibleDescription values from the UIObject search properties.
public override int GetControlSupportLevel(UITestControl uiTestControl)
{
bool useClassName = false;
string className = string.Empty;
string accDescriptionValue = string.Empty;
if(uiTestControl.SearchProperties != null && uiTestControl.SearchProperties.Count > 0)
{
PropertyExpression controlTypeExpression = uiTestControl.SearchProperties.Find(“ClassName”);
if (controlTypeExpression != null)
{
className = controlTypeExpression.PropertyValue;
useClassName = true;
}
controlTypeExpression = uiTestControl.SearchProperties.Find("AccessibleDescription"));
if (controlTypeExpression != null)
{
accDescriptionValue = controlTypeExpression.PropertyValue;
}
}
// Use this 'accDescriptionValue' and ‘className’ for all your decision making.
// Do not call uiTestControl.GetProperty("AccessibleDescription") or uiTestControl.GetProperty("className")
if (string.Equals(uiTestControl.TechnologyName, “TelerikGridView”, StringComparison.OrdinalIgnoreCase)
&& (!useClassName || string.Equals(className, TelerikGridViewConstants.TelerikGridViewClassName, StringComparison.OrdinalIgnoreCase))
&& accDescriptionValue.Contains("Telerik.WinControls.UI.GridView"))
{
return (int)ControlSupport.ControlSpecificSupport;
}
return (int)ControlSupport.NoSupport;
}
Also, note the difference between the UITestPropertyProvider.GetControlSupportLevel(UITestControl) and UITechnologyManager.GetControlSupportLevel(IntPtr). The latter is used by the recorder and Coded UI Test control locator on a live control. So it is fine to access the live information of the control (corresponding to the window handle passed) in this method.
5. Do not use the existing core technology manager names.
The Core technology managers have reserved names.
const string WindowTechnologyName = "Window";
const string MsaaTechnologyName = "MSAA";
const string WebTechnologyName = "Web";
const string UiaTechnologyName = "UIA";
const string SilverlightTechnologyName = "Silverlight";
Ensure you provide an unique name to the extension plugin manager. If there is any name conflict, the extension manager will load the plugin, but will fail to register it to the recorder or playback. As a result, the plugin is rendered useless.
6. Avoid specific implementation inside the Specialized class properties.
This is particularly of relevance if you are using the plugin for both Coded UI Test as well as MTM. The property fetcher implementation should reside on your common property provider’s UITestPropertyProvider.GetPropertyValue(UITestControl, string) implementation. The specialized class property should just be a thin layer calling into the UITestControl.GetProperty()
public class TelerikGridViewCell : WinControl
{
...
public virtual string BorderColor
{
get { return (string)this.GetProperty(PropertyNames.BorderColor); } // This internally invokes UITestPropertyProvider.GetPropertyValue().
}
}
7. Make use of the new extension proxy classes
You could be implementing support for a completely different UI Technology or you goal could be to create add-on support or customization over existing UI technologies supported by coded UI Test. For the latter, make use of the new extension proxy classes recently released that simplifies the implementation to a great extent. The details are available here.