UI Automation中查询子元素有两种方式,一种是通过TreeWalker类的GetFirstChild和GetLastChild方法,另一种是通过AutomationElement类的FindFirst和FirstAll方法,关于两者之间的性能问题,在MSDN的TreeWalker类说明中有这么一段话描述:“使用 TreeWalker 在 UI 自动化目录树中导航可能导致进程间调用,并且其效率不如使用 FindAll 或 FindFirst 方法来定位元素”。有效率上的区别,这点很关键,要知道在一个元素非常多的被测试应用程序上,有很大一部分的工作就在于定位元素,即查找元素,如果测试用例有成百上千条的时候,这两种查找元素的方式有一点效率上的差异,通过大数量级的测试用例运行后的累积效应,其测试所用总时间也会差异较大。下面通过具体的代码实现看看这两种方式效率上到底有多少差距:
说明:
1. 在如下的代码中采用Win7自带的计算器作为被测试对象,通过在依次在计算器上查询数字按钮0~9来计算出总共花费的寻找时间。
2. 本方法对TreeWalker的其他寻找元素方法,如GetParent、GetSibling,没有进行效率上的分析比较,主要是考虑到寻找子元素的情况居多。
3. 在代码中对四种方法的调用运行了两次,并且采用了正序和倒序两种方式运行,是因为发现对于TreeWalker类的GetFirstChild和GetLastChild方法,先运行的方法所花的时间要比后运行的方法要多很多,如下图所示,而AutomationElement类的FindFirst和FirstAll方法不存在这样的问题。所以进行了正序和倒序两种方式运行,以得出比较准确的运行效率区别。
class CalcAutomationClient
{
private static AutomationElement calcWindow = null;
private static Stopwatch timePerParse;
//Button 0 到 Button 9
private static TreeWalker tw0 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "130"));
private static TreeWalker tw1 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "131"));
private static TreeWalker tw2 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "132"));
private static TreeWalker tw3 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "133"));
private static TreeWalker tw4 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "134"));
private static TreeWalker tw5 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "135"));
private static TreeWalker tw6 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "136"));
private static TreeWalker tw7 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "137"));
private static TreeWalker tw8 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "138"));
private static TreeWalker tw9 = new TreeWalker(new PropertyCondition(AutomationElement.AutomationIdProperty, "139"));
static void Main(string[] args)
{
//创建新窗口打开事件的回调,只有被测试程序窗口打开后,测试才开始执行
AutomationEventHandler eventHandler = new AutomationEventHandler(OnWindowOpenOrClose);
//注册监听事件
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, eventHandler);
string appPath = @"c:/windows/system32/calc.exe";
Process.Start(appPath);
Console.ReadLine();
}
static void OnWindowOpenOrClose(object src, AutomationEventArgs e)
{
if (e.EventId != WindowPattern.WindowOpenedEvent)
return;
AutomationElement aeSourceElement = null;
try
{
aeSourceElement = src as AutomationElement;
if (aeSourceElement.Current.Name == "Calculator")
calcWindow = aeSourceElement;
}
catch (ElementNotAvailableException ex)
{
Console.WriteLine("exception:"+ex.Message);
return;
}
//下面可以开始执行真正测试
Console.WriteLine("Start testing");
Console.WriteLine("");
TraverseWithTreeWalkerGetFirstChild();
TraverseWithTreeWalkerGetLastChild();
TraverseWithAutomationElementFindAll();
TraverseWithAutomationElementFindFirst();
Console.WriteLine("");
Console.WriteLine("Reverse run again");
Console.WriteLine("");
TraverseWithAutomationElementFindFirst();
TraverseWithAutomationElementFindAll();
TraverseWithTreeWalkerGetLastChild();
TraverseWithTreeWalkerGetFirstChild();
Console.WriteLine("");
Console.WriteLine("End testing");
}
/// <summary>
/// 通过TreeWalker的方法GetFirstChild来遍历所有控件
/// </summary>
static void TraverseWithTreeWalkerGetFirstChild()
{
timePerParse = Stopwatch.StartNew(); //开始计时
tw0.GetFirstChild(calcWindow); //找寻Button0
tw1.GetFirstChild(calcWindow);
tw2.GetFirstChild(calcWindow);
tw3.GetFirstChild(calcWindow);
tw4.GetFirstChild(calcWindow);
tw5.GetFirstChild(calcWindow);
tw6.GetFirstChild(calcWindow);
tw7.GetFirstChild(calcWindow);
tw8.GetFirstChild(calcWindow);
tw9.GetFirstChild(calcWindow);
timePerParse.Stop(); //结束计时
Console.WriteLine("TreeWalker's GetFirstChild use time(Millisecond):" + timePerParse.ElapsedMilliseconds.ToString());
}
/// <summary>
/// 通过TreeWalker的方法GetLastChild来遍历所有控件
/// </summary>
static void TraverseWithTreeWalkerGetLastChild()
{
timePerParse = Stopwatch.StartNew(); //开始计时
tw1.GetLastChild(calcWindow);
tw1.GetLastChild(calcWindow);
tw2.GetLastChild(calcWindow);
tw3.GetLastChild(calcWindow);
tw4.GetLastChild(calcWindow);
tw5.GetLastChild(calcWindow);
tw6.GetLastChild(calcWindow);
tw7.GetLastChild(calcWindow);
tw8.GetLastChild(calcWindow);
tw9.GetLastChild(calcWindow);
timePerParse.Stop(); //结束计时
Console.WriteLine("TreeWalker's GetLastChild use time(Millisecond):" + timePerParse.ElapsedMilliseconds.ToString());
}
static void TraverseWithAutomationElementFindAll()
{
timePerParse = Stopwatch.StartNew(); //开始计时
int automationID=130;
//Button 0 到 Button 9
for (int i = 0; i <= 9; i++)