测试运行

在 WPF 应用程序中自动测试 UI

James McCaffrey

代码下载可从 MSDN 代码库
浏览代码联机

内容

在测试 WPF 应用程序
UI 测试自动化
最后一个单词

在本月的专栏向我展示您如何编写 UI 测试自动化的 Windows Presentation Foundation (WPF) 应用程序。WPF 应用程序使用一个新的图形子系统和大多数传统的 UI 测试自动化技术只是不能用于 WPF 应用程序。幸运的是,Microsoft UI 自动化 (MUIA) 库设计与 WPF 应用程序 UI 自动化记住。在 Microsoft 的 UI 自动化库还可以用于测试标准 Win 32 应用程序和运行支持 Microsoft.NET 3.0 Framework 支持的操作系统的主机计算机上的 WinForm 基于.NET 应用程序。

比较此项与旧替代 UI 自动化,Microsoft UI 自动化库是更强大的和更加一致,和一个初始的学习曲线后可以找到更易于使用。本专栏假设您有 WPF 应用程序、 中间级 C# 技能但 MUIA 库不熟悉一些基本熟悉。

向您介绍我在向一个好方法是使用一个屏幕快照。图 1 中的图像显示我正在测试简单但代表 WPF 应用程序。应用程序调用 CryptoCalc,并计算使用三种哈希方法之一输入字符串的加密哈希: MD 5 哈希、 SHA 1 哈希处理或 DES 加密。

fig01.gif

图 1 WPF 应用程序的 UI 自动化

在 MD 5 哈希技术 (消息摘要 5)  接受一个任意大小的字节数组,并返回一个 16 字节指纹,可用于各种标识用于。SHA 1 (安全哈希算法 1) 哈希方法与 MD 5,类似之处在于 SHA 1 将使用不同的算法,并返回 20 字节指纹。DES (标准数字加密) 是一种对称密钥加密技术,还可用于生成一个标识的字节数组。DES 加密哈希返回一个字节数组,至少与输入的字节数一样大的。

UI 测试自动化 图 1 所示是通过使用 Microsoft 的 UI 自动化库来获取应用程序 ; 上应用程序和用户控件的引用并模拟用户输入"Hello!",选择 DES 加密选项,Compute 按钮控件上单击启动待测试应用程序 ; 控制台应用程序运行的。测试自动化然后通过检查结果文本框控件的预期值检查测试应用程序的最终状态并打印一轮或失败结果。我捕获该的屏幕快照 图 1 中之前测试自动化关闭待测试应用程序模拟用户单击文件,然后退出菜单项,。

本专栏的部分的中, 我将简要介绍我正在测试 CryptoCalc WPF 应用程序,说明如何启动待测试应用程序、 如何使用 Microsoft 的 UI 自动化库获取到应用程序和用户控件的引用、 如何模拟用户操作以及如何检查应用程序状态。我还将介绍如何扩展和修改以满足您自己的需要此处提供的测试系统。我想您会发现能够使用 Microsoft 的 UI 自动化库来测试版 WPF 应用程序漂亮的添加到您的个人工具集。

在测试 WPF 应用程序

让我们一下简单测试 WPF 应用程序,以便您将了解测试自动化的目标和了解设计问题会影响 UI 测试自动化。CryptoCalc 应用程序是一个简单的、 单窗口的用户应用程序,计算字符串的加密哈希值的。应用程序接受在 TextBox 控件中的最多 24 个字符一个用户提供字符串,输入的字符串转换为字节数组,计算三种类型在输入的字节的加密哈希值之一并在第二个 TextBox 控件中显示产生的哈希的字节。

我设计测试 Visual Studio 2008 使用 C# 应用程序,并命名为项目 Crypto­Calc。在 WPF 模板生成为 XAML 文件的主干应用程序用户界面定义:

<Window x:Class="CryptoCalc.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1" Height="300" Width="300">
  <Grid></Grid>
</Window>

请注意顶级窗口控件定义不包含 name 属性。 这信息是重要中,因为正如我们将立即看到,时您编写测试自动化,获取对控件使用 MUIA 库的引用一种简便方法是访问 AutomationId 属性由从控件的名称属性编译器生成了。 没有 XAML 名称属性的控件不会收到一个 AutomationId 属性。 这个想法是重要性的考虑的内容如安全性、 可扩展性和测试自动化的应用程序设计问题一个特定的低级别示例。

接下来,我添加一个标签控件和一个 TextBox 控件通过从 Visual Studio 工具箱到设计图面的项:

<Label Height="28" HorizontalAlignment="Left"
 Margin="10,33,0,0" Name="label1" VerticalAlignment="Top"
 Width="120">Enter string:</Label>
<TextBox MaxLength="24" Height="23" Margin="10,57,51,0"
 Name="textBox1" VerticalAlignment="Top" /> 

注意默认,这些控件是否接收普通的 Name 属性 label 1 和 textBox1,分别。 接下来,我放在分组框控件中的三个 RadioButton 控件。 与该旧的 WinForms GroupBox 控件不同 WPF GroupBox 接受仅单个项,使我封装我的三个 RadioButton 控件到单个 StackPanel 容器 (它可以包含多个项目):

<GroupBox Header="Crypto Type" Margin="10,91,118,127"
 Name="groupBox1">
<StackPanel Height="52" Name="stackPanel1" Width="127">
 <RadioButton Height="16" Name="radioButton1" Width="120">
  MD5 Hash</RadioButton>
 <RadioButton Height="16" Name="radioButton2" Width="120">
  SHA1 Hash</RadioButton>
 <RadioButton Height="16" Name="radioButton3" Width="120">
  DES Encrypt</RadioButton>
</StackPanel>
</GroupBox>

接下来,我放置按钮控件来触发加密哈希计算和保存到主窗口控件上的结果一个 TextBox 控件:

<Button Height="23" Margin="10,0,0,90" Name="button1"
 VerticalAlignment="Bottom" Click="button1_Click"
 HorizontalAlignment="Left" Width="89">Compute</Button>
<TextBox Height="63" Margin="10,0,51,13" Name="textBox2"
 VerticalAlignment="Bottom" TextWrapping="Wrap" />

我非常喜欢有关 WPF 应用程序的功能之一是新的菜单控件范例,它与 WinForm 的菜单项不同将菜单和子菜单视为普通的用户控件。 首先,我拖动主菜单容器控件进行 CryptoCalc 应用程序的上一部分:

  <Menu Height="22" Name="menu1"
    VerticalAlignment="Top" IsMainMenu="True" >
  </Menu>

Visual Studio 2008 不为子菜单项控件,支持拖和设计,因此我添加我的菜单项到 XAML 定义文件手动添加一个顶层文件菜单项:

  <MenuItem Header="_File">
    <MenuItem Header="_New" Name="fileNew" />
  <MenuItem Header="_Open" Name="fileOpen" />
  <Separator />
  <MenuItem Header="E_xit" Name="fileExit"
    InputGestureText="Alt-F4" ToolTip="Exit CryptoCalc"
    Click="OnFileExit" />
</MenuItem>

WPF 设计支持通常的加速键语法,如 _File。 在 < 分隔符 / > 标记是干净简便。 该工具提示属性将生成用户 mouses 相关联的 MenuItem 控件上时显示简短的帮助消息的代码。 编译时, 单击 OnFileExit 属性将生成需要事件处理程序与此签名的 C# 代码:

public void OnFileExit(object sender, RoutedEventArgs e) {
  //...       
}

因此,我添加到 Crypto­Calc 应用程序的常规应用程序逻辑代码之后,我手动添加事件处理程序,然后由 this.Close 放方法体中提供的功能。 请注意新的和打开的菜单项控件无需在此时的任何关联的事件。 我做这指出 UI 测试的自动化将通常将放在开发过程中许多应用程序功能不完整。 顶级帮助控件设计: 和文件控件相同的图案

<MenuItem Header="_Help">
 <MenuItem Header="Help Topics" />
 <Separator />
 <MenuItem Header="About CryptoCalc" />
</MenuItem>

在菜单容器和菜单项控件定义将生成 C# 代码又生成用户界面 图 2 中屏幕快照所示。 用户界面设计完成我切换通过到代码视图。 然后我添加两个使用语句使用,以便我可以访问加密的类和方法不完全限定它们的名称由 Window1.xaml.cs 文件中的 Visual Studio 的语句:

using System.Security.Cryptography;
using System.IO;

fig02.gif

图 2 WPF 应用程序文件菜单

简单 CryptoCalc 应用程序的主要功能包含在 button 1 _ Click 方法中,, 图 3 中列出。 首先,我的代码获取 textBox1 控件中的文本,并将该文本转换为字节数组。 请注意若要使我的示例代码简短大小,我忽略该常规错误检查您要执行生产环境。

图 3 StatCalc 应用程序代码

private void button1_Click(object sender, RoutedEventArgs e)
{
  string input = textBox1.Text;
  byte[] inputBytes = Encoding.UTF8.GetBytes(input);

  if (radioButton1.IsChecked == true) { 
    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
    byte[] hashedBytes = md5.ComputeHash(inputBytes);
    textBox2.Text = BitConverter.ToString(hashedBytes);
  }
  else if (radioButton2.IsChecked == true) { 
    SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider();
    byte[] hashedBytes = sha.ComputeHash(inputBytes);
    textBox2.Text = BitConverter.ToString(hashedBytes);
  }
  else if (radioButton3.IsChecked == true) { 
    DESCryptoServiceProvider des = new DESCryptoServiceProvider();
    byte[] blanks = System.Text.Encoding.UTF8.GetBytes("        "); // 8 spaces
    des.Key = blanks;
    des.IV = blanks;
    des.Padding = PaddingMode.Zeros;
    MemoryStream ms = new MemoryStream();
    CryptoStream cs =
      new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
    cs.Write(inputBytes, 0, inputBytes.Length);
    cs.Close();
    byte[] encryptedBytes = ms.ToArray();
    ms.Close();
    textBox2.Text = BitConverter.ToString(encryptedBytes);
  }
} 

应用程序逻辑分支的 RadioButton 根据选定控件。有趣的是,因为 IsChecked 属性返回类型? bool (可空布尔值),我必须显式检查针对 True 相等性属性。该代码对于 MD 5 和 SHA 1 哈希应容易理解。

DES 加密算法需要 64 位密钥。由于我使用 DES 而不是编码和解码哈希,我将使用虚拟键由 8 个空格字符。在使用对称密钥加密哈希目的时, 我通常使用的空键 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},但 DESCryptoServiceProvider 对象此标志为已知弱键,将引发异常。我使用同一个空白空间字节数组为所谓的初始化向量提供值。

DES 加密适用于 8 字节的块,; 因此,我指定 PaddingMode.zeros 以便任何输入将被填充用零将输入达 8 个字节的偶数倍数的大小。请注意您不会使用 PaddingMode.zeros 进行编码,解码因为用零填充不允许您来解密 (解密算法不能确定密钥的文本中的零会填充,这是原始的纯文本的一部分)。

UI 测试自动化

编写测试自动化使用 Microsoft 的 UI 自动化库时, 必须了解如何识别待测试应用程序上的控件。这样做一个好方法是使用 UISpy 工具。对于那些不知道,UISpy 等效于旧的 Spy++ 工具 WPF 并允许您检查的 WPF 应用程序用户界面组件的属性。该 UISpy 工具是 Microsoft Windows SDK 的一部分并且可作为从免费下载Microsoft.com/downloads.

图 4 中的屏幕快照显示了针对 CryptoCalc 应用程序上的 Button 控件的结果。从测试自动化透视,重要字段对于识别应用程序控件将包括 ControlType (ControlType.Edit 在这种情况下)、 AutomationId (button 1) 和名称 (计算) 值。操作应用程序控件的重要域是 ControlPatterns 列表 (调用)。

fig04.gif

图 4 检查具有 UISpy 控件

手动测试应用甚至此小 CryptoCalc 程序通过其用户界面应为人、 容易出错,耗时,和效率很低。将必须输入一个输入,单击计算按钮控件、 直观地验证该的答案手动记录通过还是失败结果。多更好的方法是使用 Microsoft 的 UI 自动化库编写测试自动化的模拟用户实施其应用程序,然后确定该应用程序是否正确响应。通过自动化令人乏味的工作的测试用例,可以释放更有趣且有用手动测试用例您的经验和直觉发挥大作用的时间。

图 5 中列出的测试工具生成输出 图 1 所示的总体结构。我启动 Visual Studio 2008,并创建新的控制台应用程序。我使用 C#,但是应能够轻松地将我的测试自动化代码转换为 Visual Basic.NET,如果想要。接下来,我添加到 UIAutomationClient.dll 和 UIAutomationTypes.dll 库的项目引用。这些库是.NET Framework 3.0 的一部分,但不对 Visual Studio 项目中默认情况下可见。这些库通常位于 C:\Program Files Files\Reference Assemblies\Microsoft\Framework\v3.0 目录中。请注意在 UIAutomationClient.dll 库包含密钥的类所需的测试自动化。UIAutomationTypes.dll 库包含由 MUIA 测试自动化的各种类型定义。

图 5 UI 测试自动化代码结构

using System;
using System.Diagnostics; 
using System.Threading; 
using System.Windows.Automation; 

namespace Harness {
  class Program {
    static void Main(string[] args) {
      try {
        Console.WriteLine("\nBegin WPF UIAutomation test run\n");
        // launch CryptoCalc application
        // get reference to main Window control
        // get references to user controls
        // manipulate application
        // check resulting state and determine pass/fail
        Console.WriteLine("\nEnd automation\n");
      }
      catch (Exception ex) {
        Console.WriteLine("Fatal: " + ex.Message);
      }
    } // Main()
  } // class
} // ns

为方便起见,我添加使用指向 System.Diagnostics 命名空间 (因此我可以轻松地使用 Process 类),而 System.threading 命名空间 (因此我可以轻松地使用 thread.Sleep() 方法) 的语句。 同往常一样与任何测试自动化,我封装我工具与处理任何致命的错误的顶级的 try-catch 块。 我的测试自动化代码首先启动待测试应用程序:

Console.WriteLine("Launching CryptoCalc application");
Process p = null;
p = Process.Start("..\\..\\..\\CryptoCalc\\bin\\Debug\\CryptoCalc.exe");

现在,我尝试任何测试自动化之前,我需要验证主机上已注册与测试 CryptoCalc 应用程序关联的过程。 尽管我可以通过只需插入 thread.Sleep 语句暂停我的测试工具,我有好无法知道时间若要暂停。 更好的方法是使用智能延迟循环:

int ct = 0;
do {
  Console.WriteLine("Looking for CryptoCalc process. . . ");
  ++ct;
  Thread.Sleep(100);
} while (p == null && ct < 50);

此处我暂停 100 毫秒每次延迟循环。 如果 Process 对象成为非空,这意味着在发现过程,或者如果循环执行 50 次测试工具将退出延迟循环。 找不到 图 6 显示如何我可以确定如果延迟循环超时或在 AUT 过程。

图 6 确定发生了什么变化

if (p == null)
  throw new Exception("Failed to find CryptoCalc process");
else
  Console.WriteLine("Found CryptoCalc process");

// Next I fetch a reference to the host machine's Desktop as an
// AutomationElement object:

Console.WriteLine("\nGetting Desktop");
AutomationElement aeDesktop = null;
aeDesktop = AutomationElement.RootElement;
if (aeDesktop == null)
  throw new Exception("Unable to get Desktop");
else
  Console.WriteLine("Found Desktop\n");

您可以将 WPF 应用程序测试作为子级主应用程序窗口控件的所有控件。 主窗口是桌面的在整个的子,因此我需要对桌面的引用中才获取对应用程序的引用。 现在,我使用 FindFirst 方法将附加到待测试应用程序:

AutomationElement aeCryptoCalc = null;
int numWaits = 0;
do {
  Console.WriteLine("Looking for CryptoCalc main window. . . ");
  aeCryptoCalc = aeDesktop.FindFirst(TreeScope.Children,
    new PropertyCondition(AutomationElement.NameProperty, "CryptoCalc"));
  ++numWaits;
  Thread.Sleep(200);
} while (aeCryptoCalc == null && numWaits < 50);

我使用智能延迟循环方法而不是任意长度的睡眠方法时。 要查找该控件是单个控件时,使用 FindFirst 方法。 FindFirst 接受两个参数。 第一个是作用域值。 测试自动化中使用在三个最常见作用域类型包括 TreeScope.children、 TreeScope.descendants,和 TreeScope.parent。 由于 CryptoCalc 应用程序是在桌面的直接子级,我使用 TreeScope.children 范围。

FindFirst 将第二个参数是代表用于识别您在寻找该控件的信息的对象。 在这里我指定我正在查找具有"CryptoCalc"的值的 Name 属性的一个控件。 我可能还具有用作 AutomationId 属性稍后我将介绍。 现在我验证实际上具有对主应用程序窗口控件的引用:

if (aeCryptoCalc == null)
  throw new Exception("Failed to find CryptoCalc main window");
else
  Console.WriteLine("Found CryptoCalc main window");

一旦我将该应用程序,我可以使用它的引用获取测试自动化操作或检查所有用户控件的引用。 我首先获取按钮控件:

Console.WriteLine("\nGetting all user controls");
AutomationElement aeButton = null;
aeButton = aeCryptoCalc.FindFirst(TreeScope.Children,
  new PropertyCondition(AutomationElement.NameProperty, "Compute"));
if (aeButton == null)
  throw new Exception("No compute button");
else
  Console.WriteLine("Got Compute button");

我使用我用来获取对主窗口的引用的同一模式 ; 此处按钮控件的主窗口的应用程序控制直接子级。 因为按钮控件是静态控件,我不需要访问对控件引用之前使用延迟循环技术。 在动态的控件的情况下应使用延迟循环方法。

接下来,我想获取两个 TextBox 控件的引用。 即使两个 TextBox 控件具有名称 textBox1 和 textBox2,控件将不会名称属性,因此我不能使用同一 NameProperty 模式,我使用的按钮控件。 但是,TextBox 控件收到的 AutomationId 属性可以使用来获取对该的控件的引用,如:

aeTextBox1 = aeCryptoCalc.FindFirst(TreeScope.Children,
  new PropertyCondition(AutomationElement.AutomationIdProperty, "textBox1"));

相反,我决定主要是出于演示目的使用不同的方法。 而不是标识单个控件使用该控件的 Name 属性,我是使用 FindAll 方法按控件类型获取控件的集合。 事实证明 TextBox 控件将是 ControlType.Edit 类型,以便我的代码中获取所有 TextBox 控件:

AutomationElementCollection aeAllTextBoxes = null;
aeAllTextBoxes = aeCryptoCalc.FindAll(TreeScope.Children,
  new PropertyCondition(AutomationElement.ControlTypeProperty,
  ControlType.Edit));
if (aeAllTextBoxes == null)
  throw new Exception("No textboxes collection");
else
  Console.WriteLine("Got textboxes collection");

我获得了我可以访问该集合后,每个 TextBox 使用数组索引:

AutomationElement aeTextBox1 = null;
AutomationElement aeTextBox2 = null;
aeTextBox1 = aeAllTextBoxes[0];
aeTextBox2 = aeAllTextBoxes[1];
if (aeTextBox1 == null || aeTextBox2 == null)
  throw new Exception("TextBox1 or TextBox2 not found");
else
  Console.WriteLine("Got TextBox1 and TextBox2");

通常,使用 Name 属性或将 AutomationId 属性获取控件的引用是一种更好的方法比使用 ControlType,但在某些情况下您可能必须无选择。 下一步,我获取对我要在我的测试方案中使用该 RadioButton 控件的引用:

AutomationElement aeRadioButton3 = null;
aeRadioButton3 = aeCryptoCalc.FindFirst(TreeScope.Descendants,
  new PropertyCondition(AutomationElement.NameProperty,
   "DES Encrypt"));
if (aeRadioButton3 == null)
  throw new Exception("No RadioButton");
else
  Console.WriteLine("Got RadioButton3");

我使用类似于我用来获取对按钮控件的引用模式。 但是,请注意我指定 TreeScope.descendants,而不是 TreeScope.children。 我这样因为 RadioButton 控件分组框控件和操作的子级不是主窗口控件的直接子。 或者,我可能有第一次获取 GroupBox 控件对 (作为主窗口控件的子级),然后使用该引用获取 RadioButton 控件对引用。 一旦我了我的控件的引用,我可以启动操作待测试应用程序。 我首先模拟用户输入到 TextBox 控件:

Console.WriteLine("\nSetting input to 'Hello1!'");
ValuePattern vpTextBox1 =
  (ValuePattern)aeTextBox1.GetCurrentPattern(ValuePattern.Pattern);
vpTextBox1.SetValue("Hello!");

使用 SetValue 方法可能会不是,一个意外,但是我不通过 aeTextBox1 对象直接访问 SetVaue() 的通知。 相反,我使用中间 ValuePattern 对象。 如 ValuePattern AutomationPattern 对象的概念可能是最大的概念障碍的工程师新到 Microsoft 的 UI 自动化库。 可以将模式对象视为抽象公开独立于该控件的类型或外观的控件的功能。 换句话说,可以使用特定 AutomationPattern 实例,如 ValuePattern 以启用特定控件的功能。

我想控件的 ControlType 的公开哪些类型的控件的控件,并且控件的模式提供该控件可以做些简化了进一步的操作。 我用类似方法模拟用户选择 RadioButton3 控件:

Console.WriteLine("Selecting 'DES Encrypt' ");
SelectionItemPattern spSelectRadioButton3 =
  (SelectionItemPattern)aeRadioButton3.GetCurrentPattern(
    SelectionItemPattern.Pattern);
spSelectRadioButton3.Select();

这次我使用该 SelectionItemPattern 启用选定内容。 GetCurrentPattern 方法的名称有时 confuses MUIA 库初学者。 从测试自动化的角度,方法是设置,不能获取指定的 AutomationPattern。 但是,从客户端-服务器的角度自动化客户端代码从测试服务器代码在应用程序获取特定属性。

我使用来模拟计算按钮控件上的单击该代码应帮助阐明:

Console.WriteLine("\nClicking on Compute button");
InvokePattern ipClickButton1 =
  (InvokePattern)aeButton.GetCurrentPattern(
    InvokePattern.Pattern);
ipClickButton1.Invoke();
Thread.Sleep(1500);

此处,实际上,我使用该 InvokePattern 来启用按钮单击,然后通过使用 Invoke 方法中执行的单击。 请注意,我为我应用程序时间响应暂停 1.5 秒。 我可能还进入一个延迟循环会定期检查以确定结果 textBox2 字段是否为空。 到目前为止在我的测试自动化代码,我有启动待测试应用程序,输入"Hello!"在输入的 TextBox 控件选择在 DES 加密 RadioButton 控件并单击计算按钮控件。

现在我检查 2 的 TextBox 控件查看我是否具有正确的预期值:

Console.WriteLine("\nChecking TextBox2 for '91-1E-84-41-67-4B-FF-8F'");
TextPattern tpTextBox2 =
  (TextPattern)aeTextBox2.GetCurrentPattern(TextPattern.Pattern);
string result = tpTextBox2.DocumentRange.GetText(-1);

此处,我使用 TextPattern 准备对 GetText 方法的调用。 注意我调用 GetText 间接通过返回将主文档文本的一个,在这种情况下一个文本框的文本区域 DocumentRange 属性。 GetText-1 参数使用,所以返回的字符串的长度没有最大限制。 读取 2 的 TextBox 控件的内容的替代方法是使用 GetCurrentPropertyValue 方法:

string result =
  (string)aeTextBox2.GetCurrentPropertyValue(ValuePattern.ValueProperty);

我有硬编码测试用例输入测试工具。 一个更灵活的方法是一些外部数据存储区中读取测试用例输入和预期值。 现在,与现有库中的测试应用程序的实际值,我检查以确定我的测试方案通过还是失败结果的预期值:

if (result == "91-1E-84-41-67-4B-FF-8F") {
  Console.WriteLine("Found it");
  Console.WriteLine("\nTest scenario: Pass");
} 
else {
  Console.WriteLine("Did not find it");
  Console.WriteLine("\nTest scenario: *FAIL*");
}

我只是显示在命令行解释器测试方案结果。 在生产环境中您通常需要写入外部存储测试结果。

我的测试方案完成,我可以通过实施其 Menu 控件关闭待测试应用程序。 首先,出现顶层文件 MenuItem 控件:

Console.WriteLine("\nClicking on File-Exit item in 5 seconds");
Thread.Sleep(5000);
AutomationElement aeFile = null;
aeFile = aeCryptoCalc.FindFirst(TreeScope.Descendants,
  new PropertyCondition(AutomationElement.NameProperty, "File"));
if (aeFile == null)
  throw new Exception("Could not find File menu");
else
  Console.WriteLine("Got File menu");

请注意我使用 TreeScope.descendants 范围,因为文件 MenuItem 控件是一个 Subcontrol 菜单容器控件。 现在我将模拟用户单击文件项:

Console.WriteLine("Clicking on 'File'");
ExpandCollapsePattern expClickFile =
  (ExpandCollapsePattern)aeFile.GetCurrentPattern(ExpandCollapsePattern.Pattern);
expClickFile.Expand();

正如您所期望的那样,具有子菜单的 MenuItem 控件不公开的调用模式 ; 它们公开的扩展模式。 具有文件子菜单项现在呈现可见,我可以获取对退出命令的引用:

AutomationElement aeFileExit = null;
aeFileExit = aeCryptoCalc.FindFirst(TreeScope.Descendants,
  new PropertyCondition(AutomationElement.NameProperty, "Exit"));
if (aeFileExit == null)
  throw new Exception("Could not find File-Exit");
else
  Console.WriteLine("Got File-Exit");

现在我可以使用一个 InvokePattern 退出子菜单上关闭待测试的 CryptoCalc 应用程序:

InvokePattern ipFileExit =
  (InvokePattern)aeFileExit.GetCurrentPattern(InvokePattern.Pattern);
ipFileExit.Invoke();
Console.WriteLine("\nEnd automation\n");

我的测试自动化完成到目前为止我可以并记录测试案例结果启动另一个测试方案。

最后一个单词

本月的专栏中提供的代码可以很好的基础的入门创建 WPF 应用程序的自定义测试自动化。 MUIA 库是非常广泛,并可以处理最简单的测试方案。

该模式以适应简单的示例我已经此处提供要测试您自己的 WPF 应用程序非常简单。 当创建您的 WPF 应用程序,尝试确保所有控件都具有 XAML name 属性,以便生成一个 AutomationID。 使用 UISpy 工具来确定如何识别和操作用户控件。 确定 MUIA 模式允许您检查状态和用户控件的值。 用手中的此信息,可以处理最基本的 UI 测试自动化方案。

在所有测试的情况下,您必须认真权衡所需创建测试自动化对从自动获得的好处付出的精力。 根据我的经验,WPF 的 UI 测试自动化通常最好用于回归测试的相对简单的方案。 这可以将精力集中在复杂的方案测试您手动和而不必担心丢失开发添加新功能时意外地引入的一个明显错误查找新的细微的错误。

作为常规经验的轻量测试此列中, 描述的自动化类型我已发现是否我的测试自动化时间少于 4 小时创建,然后得到合理值返回我的时间投资。 当然您的环境将不同,关键是您应该不会自动假定 UI 测试自动化是始终最可能将您的测试资源。 WPF 仍是一种相对较新的技术。 为 WPF 应用程序增加的存在,UI 测试自动化,但本专栏中介绍的方法很可能会变得越来越多地用于要创建更好的软件。

JamesMcCaffrey 博士 适用于 Volt Information Sciences,Inc.,他管理在 Microsoft 的 Redmond 华盛顿校园工作的软件工程师的技术培训。 他有效多种 Microsoft 产品包括 Internet Explorer 和 MSN Search。 James 是 .NET Test Automation Recipes (Apress,2006) 的作者。 James 可以访问在 jmccaffrey@volt.comv-jammc@Microsoft.com.