正文
关于UI Automation框架
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
微软提供的UI Automation框架给开发windows平台的自动化测试带来了很大的便利,这里就总结一下相关的代码。
首先,直接使用UI Automation框架,完成一个NotePad的about窗口中的 “OK” button的点击:
AutomationElement root = AutomationElement.RootElement;
AutomationElement about_notepad_windows = root.FindFirst(
TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window),
new PropertyCondition(AutomationElement.NameProperty, "About Notepad")));
if (about_notepad_windows == null)
{
Console.WriteLine("About Notepad window doesn't exist!!");
return;
} AutomationElement ok_button = about_notepad_windows.FindFirst(
TreeScope.Children,
new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
new PropertyCondition(AutomationElement.NameProperty, "OK")));
Object invokePatternObject;
if (ok_button.TryGetCurrentPattern(InvokePattern.Pattern, out invokePatternObject))
{
(invokePatternObject as InvokePattern).Invoke();
}
好吧,上面是面向过程的代码,不利于复用,那么让我们来将其抽象成类,
首先定义一个基类UIAControl,它包含有搜索的根节点searchRoot,搜索范围searchScope和条件searchConditions,使用这三个对象来搜索一个AutomationElement对象并将其赋给innerElement,由于默认使用的是AndCondition来关联所有传入的condition对象,所以将CombineCondition方法设为虚方法,以便如果有子类想要使用其他关联条件处理condition的时候可以覆盖:
public abstract class UIAControl
{
private UIAControl searchRoot;
private TreeScope searchScope;
private List<Condition> searchConditions; protected void AddSearchCondition(Condition condition)
{
this.searchConditions.Add(condition);
} public UIAControl()
{
searchConditions = new List<Condition>();
searchScope = TreeScope.Descendants;
} public UIAControl(UIAControl searchRoot)
: this()
{
this.searchRoot = searchRoot;
} public UIAControl(IntPtr hwnd)
: this()
{
searchConditions.Add(PropertyConditionFactory.GetHandleCondition(hwnd));
} public AutomationElement.AutomationElementInformation? ControlInformation
{
get
{
if (Exists())
{
return InnerElement.Current;
}
return null;
}
} private AutomationElement innerElement;
public AutomationElement InnerElement
{
get
{
if (innerElement == null)
{
innerElement = SearchElement();
}
return innerElement;
}
} protected virtual AutomationElement SearchElement()
{
AutomationElement ele = null;
if (searchRoot == null)
{
ele = AutomationElement.RootElement;
}
else
{
ele = searchRoot.InnerElement;
} if (ele == null || == searchConditions.Count)
{
return ele;
}
else
{
Condition conditions = CombineAllConditions();
try
{
return ele.FindFirst(searchScope, conditions);
}
catch(Exception ex)
{
Console.WriteLine("Getting exception when searching element: " + ex.Message);
return null;
}
}
} //Can override this method to return other type conditions, default will return AndCondition
protected virtual Condition CombineAllConditions()
{
if (searchConditions.Count > )
{
return new AndCondition(searchConditions.ToArray());
}
else if (searchConditions.Count == )
{
return searchConditions.First();
}
else
{
return null;
} } public virtual bool Exists()
{
//Before checking existence, set innerElement to null to trigger fresh search.
return null != SearchElement();
} public bool WaitTillExist(int timeout, int interval = )
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < timeout)
{
if (this.Exists())
{
return true;
} Thread.Sleep(interval);
} return false;
} protected bool CallPattern<T>(T pattern, Action<T> action) where T : BasePattern
{
if (pattern != null)
{
try
{
action(pattern);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return false;
} protected T GetPattern<T>() where T : BasePattern
{
var ele = InnerElement;
if (ele != null)
{
try
{
var patternIdentifier = (AutomationPattern)(typeof(T).GetField("Pattern").GetValue(null));
return ele.GetCurrentPattern(patternIdentifier) as T;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return null;
} public bool Invoke()
{
return CallPattern(GetPattern<InvokePattern>(), (pattern) => pattern.Invoke());
}
}
之后,就可以定义其他控件类型了,下面定义了一个button的对象,只有一个click方法:
public class UIAButton : UIAControl
{
public UIAButton(UIAControl root, string name)
: base(root)
{
AddSearchCondition(PropertyConditionFactory.GetNameCondition(name));
AddSearchCondition(PropertyConditionFactory.GetControlTypeCondition(ControlType.Button));
} public void Click()
{
this.Invoke();
}
}
再定义一个window对象:
public class UIAWindow : UIAControl
{
public UIAWindow(string name)
{
AddSearchCondition(PropertyConditionFactory.GetNameCondition(name));
AddSearchCondition(PropertyConditionFactory.GetControlTypeCondition(ControlType.Window));
} public bool Close()
{
return CallPattern(GetPattern<WindowPattern>(), (pattern) => pattern.Close());
} public bool Maximize()
{
return CallPattern(GetPattern<WindowPattern>(), (pattern) => pattern.SetWindowVisualState(WindowVisualState.Maximized));
} public bool Minimize()
{
return CallPattern(GetPattern<WindowPattern>(), (pattern) => pattern.SetWindowVisualState(WindowVisualState.Minimized));
} public bool Resize(int width, int height)
{
return CallPattern(GetPattern<TransformPattern>(), (pattern) => pattern.Resize(width, height));
}
}
最后,使用上面两个控件类型来完成Ok按钮的点击:
UIAWindow windows = new UIAWindow("About Notepad");
UIAButton ok_button = new UIAButton(windows, "OK");
if (windows.WaitTillExist())
{
ok_button.Click();
}
当然,如果是稍微上点规模的项目,就需要利用这些控件抽象出一个对应产品功能的produc类了。
如果不想从头写起的话,可以看看开源工具White, 一个优秀的基于UI Automation的测试框架。