在大多数情况下,获得对所测试应用程序的窗体对象(作为 AutomationElement)引用的更好方法是使用类似Figure 5 中所示的代码。我就如何以很小的延迟(在本例中为 100 毫秒)在循环内部获得引用进行了尝试,在盲目请求对应用程序的引用之前无需任意时间的暂停。我跟踪我的工具进入等待循环的次数,在获得非空引用(意味着我找到了窗体对象)时,或在超过最大尝试次数(在本例中为 50)时退出。
Figure 5 获取测试应用程序 AutomationElement // a better approach while (aeForm == null && numWaits < 50); if (aeForm == null) |
此外,我使用 FindFirst 方法,而不使用 FromHandle,前者能够继续搜索对 AutomationElement 的第一个可用的引用,AutomationElement 满足作为一对参数传入的特定条件。我的第一个参数 TreeScope.Children 指示 FindFirst 仅查看其上下文(在本例中为 aeDesktop)的直接子控件,而不需查看其上下文的所有后代。
FindFirst 的第二个参数是一个条件:
new PropertyCondition(AutomationElement.NameProperty, "StatCalc") |
可以将此代码的含义解释如下:“标题(Name 属性)的自动化元素等于 StatCalc。”不需要在条件中使用传统的字符串方法来确定控件(例如,Property == StatCalc),UI 自动化库的设计人员决定创建 PropertyCondition 类。这一方法乍看可能有些难以理解且冗繁,但是您很快就能熟悉它。
基本上,使用完全面向对象的 PropertyCondition 类(而不使用简单的字符串)来确定控件在编写测试自动化时需要更多代码,但是它也有多个优点。PropertyCondition 往往使测试自动化代码的意图非常明确(至少在您熟悉此模式后是这样的)。因为 PropertyCondition 对象较之字符串是更强类型化的,所以使用 PropertyCondition 允许集成的开发环境(如 Visual Studio)提供设计时 IntelliSense® 自动完成帮助。它还使编译器能够执行更完善的错误检查。
获得对所测试应用程序的主窗体对象的引用后,我获取了对所有用户控件的引用。例如,在需要获得对“Calculate”(计算)按钮控件的引用时,我可以编写如下代码:
Console.WriteLine("Finding all user controls"); AutomationElement aeButton = aeForm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Calculate")); |
获得用户控件的模式与获得窗体控件的模式是完全相同的。这种一致性是 UI 自动化库较之许多其他自动化库的一个优点。对于本例中这样使用大量编码类型、使用 UI 自动化的情况,通常可以使用几种不同的方法来完成任务。例如,若不使用上述代码获得对“Calculate”(计算)按钮控件的引用,我还可以编写如下代码:
AutomationElement aeButton = aeForm.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)); |
我可以不使用 NameProperty,而使用 ControlTypeProperty 及 ControlType.Button 的值。可以将此代码的含义解释为:“提取在窗体上找到的第一个按钮控件。”此方法仅在此处适用,因为我的窗体对象上只有一个按钮控件。
ControlType 类是一种重要的 Microsoft UI 自动化思路。此类非常简单,可用于确定任何控件的类型。获得对我的既没有标题也没有名称的文本框控件的引用,需要不同的技术:
AutomationElementCollection aeAllTextBoxes = AutomationElement aeTextBox1 = aeAllTextBoxes[1]; |
这里,我使用 FindAll 方法来检索所有作为我的窗体控件的直接子项的文本框控件。注意,您可能已经猜到,我使用 ControlType.Edit 来指定文本框控件,而不是类似于 ControlType.TextBox 的控件。我将我的所有文本框控件存储在 AutomationElementCollection 中,然后可以按索引访问这些控件。
请回忆一下先前介绍的内容:textBox2 控件具有低于 textBox1 控件的隐含的索引,因此 textBox2 位于集合中的位置 [0],而 textBox1 位于集合中的位置 [1]。还可以使用其他几种方法来获得对没有标题的控件的引用,但是 FindAll 方法往往是不错的选择。
以下是我获取对 radioButton1 控件的引用的方法:
AutomationElement aeRadioButton1 = aeForm.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Arithmetic Mean")); |
请注意,因为我当前的上下文是窗体对象,所以我使用 TreeScope.Descendants,而不使用 TreeScope.Children。radioButton 控件是 groupBox1 控件的子项,后者又是 Form1 控件的子项,因此 radioButton 控件是 Form1 的后代,但不是它的子项。或者,我可以首先获得对 groupBox1 控件(为 Form1 的子项)的引用,然后再获得对 radioButton1(为 groupBox1 控件的子项)的引用。
在采用上述获得对 radioButton1 的引用的方法获得对 radioButton2(几何平均数)和 radioButton3(调和平均数)的引用后,我可以开始执行所测试的应用程序。