当前位置:编程学习 > C#/ASP.NET >>

用 .NET 开发的轻量级 UI 测试自动化

答案:James McCaffrey

下载本文的代码: TestRun0501.exe (131KB)



本页内容
待测试应用程序
测试自动化脚本
操作待测试应用程序
检查应用程序状态
讨论

手动用户界面测试是一种最基本的软件测试类型,大多数软件工程师首次采用的就是这种测试类型。与此矛盾的是,自动化用户界面测试可能是编写的测试类型中最具技术挑战的一种。Microsoft® .NET 环境为您提供了许多编写自动用户界面测试自动化的方式。一种常见而有用的方法是记录击键、鼠标移动和单击,然后在应用程序中回放以确保它以预期方式执行。(有关这种方法的详细信息,请参见 MSDN®Magazine 2002 年 3 月号中 John Robbins 的 Bugslayer 专栏。这一期 MSDN Magazine 的 Paul DiLascia 专栏也阐释了如何使用 .NET 将这种类型的输入发送到另一个应用程序中。)在本月的专栏中,我将探讨为 .NET 应用程序编写轻量级 UI 测试自动化的另一种方法。

最好的方式是以一个屏幕快照开始进行讨论。图 1 显示我有一个虚拟应用程序要进行测试。它是一个颜色合成器应用程序,允许用户在文本框控件中键入一种颜色,然后在 Combobox 中键入或选择一种颜色,单击按钮,Listbox 就会显示一条消息,表明两种颜色“混合”的结果。在图 1 中,根据应用程序,红色和蓝色会产生紫色。UI 测试自动化是一个控制台应用程序,它启动一个待测试窗体,模拟用户移动应用程序窗体,定义和调整应用程序窗体的大小,设置文本框和 Combobox 控件的值,并单击按钮控件。测试自动化检查测试应用程序的结果状态,验证 Listbox 控件包含正确的消息,并记录“pass”结果。图 1 中的屏幕快照是在测试自动化模拟用户单击关闭测试应用程序的 File | Exit 之前捕获的。



图 1 窗体 UI 测试自动化


在下面的章节中,我将简要介绍我所测试的虚拟应用程序,解释如何使用反射和 System.Windows.Forms.Application 类启动测试自动化程序中的应用程序窗体,介绍如何使用 System.Reflection 命名空间中的方法模拟用户操作和检查应用程序状态,并描述如何扩展和修改测试系统来满足自己的需要。我想,不管您在软件生产环境中扮演什么样的角色,具备快速编写轻量级 UI 测试自动化的能力都能使您的技能得到很大提高。另外,即使您正在使用一个已有的框架(如 Nunit),这些相同的技术也可以整合到您自己的单元测试管理中并相关。

待测试应用程序


让我们来看一下待测试应用程序,以便理解测试自动化的目标。待测试颜色合成器应用程序是一个简单的 Windows® 窗体。应用程序的代码是使用 C# 编写的,但我将向您介绍的 UI 自动化技术适用于用任何以 .NET 为目标的语言编写的应用程序。我接受 Visual Studio® .NET 默认控件名称 Form1、textBox1、comboBox1、button1 和 listBox1。当然,在实际的应用程序中,您应该更改控件的名称来反映它们的功能。我添加了三个虚拟菜单项:File、Edit 和 View。图 2 中列出的代码是待测试应用程序的主要内容。

当用户单击 button1 控件时,应用程序就会获取 textBox1 和 comboBox1 控件中的值。如果这两个颜色字符串匹配,就会显示这种颜色的消息。如果文本框和 Combobox 控件分别包含“red”和“blue”,则显示结果消息“purple”。如果文本框和 Combobox 控件中为其他任何颜色组合,则显示结果消息“black”。因为这只是用于演示的虚拟应用程序,我想让代码尽可能简短,所以没像在实际应用程序中那样检查输入参数。虽然这个应用程序非常简单,但它具备了演示自动化 UI 测试所需要的基于 Windows 应用程序的大多数基本特征。

即使对于如此小的应用程序,要手动测试它的用户界面也是很繁琐、易出错、耗时且又低效的。您必须键入一些输入,单击按钮控件,直观验证结果消息,并将结果记录到 Excel 电子表格或其他数据存储中。因为应用程序接受用户在文本框控件中的自由输入,实际上可能的测试输入是无限的,所以您必须测试上百甚至上千个输入才能很好地理解应用程序的行为。对于上述所有操作,一旦应用程序代码有所更改,您就必须从头执行相同的手动测试。编写单元测试是一个更好的方法,因为这些测试允许您模拟使用该应用程序的用户,然后确定应用程序是否正确响应。

返回页首
测试自动化脚本


图 3 显示了测试自动化管理的整体结构,图 4 显示了代码大纲。这里我使用了 C#,但您可以很容易地将代码修改为任何基于 .NET 的语言。



图 3 UI 测试自动化结构


我首先添加和声明 System.Windows.Forms、System.Reflection 和 System.Threading 命名空间的引用。因为默认情况下控制台应用程序不引用 System.Windows.Forms.dll,所以要使用这个命名空间中的类,就需要添加对 System.Windows.Forms.dll 文件的项目引用。System.Windows.Forms 命名空间包含 Forms 类和 Application 类,它们在这个解决方案中都有使用。我使用 System.Reflection 中的类来获取和设置窗体控件的值并调用与该窗体相关的方法。使用 System.Threading 中的方法来从控制台应用程序测试管理启动窗体。

我声明了三个类作用范围对象,因为在测试管理中有几个方法要用到它们:

static Form testForm = null;
static BindingFlags flags = BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Static | BindingFlags.Instance;
static int delay = 1500;

因为颜色合成器应用程序只是一个 Windows 窗体,所以我声明一个 Form 对象来表示它。System.Reflection 命名空间中有许多方法将 BindingFlags 对象作为筛选器使用。我为 Thread.Sleep 方法设置了一个值为 1500(毫秒)的整型延时变量,这样在测试自动化的各个时刻都可以暂停 1.5 秒。我使用这段代码来启动待测试应用程序:

Console.WriteLine("\nLaunching WinApp under test");
string exePath = "C:\\FormUIAutomation\\WinApp\\bin\\Debug\\WinApp.exe";
testForm = LaunchApp(exePath, "WinApp.Form1");


我定义了图 5 中的 LaunchApp 方法及其 helper 方法 RunApp。这段代码的行数不多,但作用很大。请注意,为了简单起见,我硬编码了指向待测试应用程序可执行文件的路径(在您自己的测试中,您可能想用参数表示这个信息以使得测试自动化更加灵活)。LaunchApp 方法接受应用程序可执行文件的路径和应用程序窗体的名称,并返回一个表示该窗体的对象。LaunchApp 使用 Assembly.LoadFrom 静态方法创建 Assembly 对象的实例,而不是通过显式调用构造函数来创建。

接下来,Assembly.GetType 方法返回一个表示应用程序窗体的类型,然后我使用 Assembly.CreateInstance 方法创建对待测试窗体的引用。然后我发起一个新的线程来实际启动应用程序窗体。Application.Run 方法开始在当前线程进行消息循环;由于我想在窗体可见时执行工作,所以我需要使 Application.Run 在自己的线程中运行,这样循环才不会阻止我的进程。通过使用这种技术,测试自动化控制台应用程序管理和窗体就会在不同线程但相同的进程下运行。这种方式可以使它们相互通信 — 也就是说,测试管理可以发送指令给 Windows 窗体。

返回页首
操作待测试应用程序


当启动待测试应用程序后,我模拟用户操作应用程序窗体。示例测试方案是从移动窗体和调整窗体大小开始的,如下所示:

Console.WriteLine("Moving form");
SetFormPropertyValue("Location", new System.Drawing.Point(200,200));
SetFormPropertyValue("Location", new System.Drawing.Point(500,500));

Console.WriteLine("Resizing form");
SetFormPropertyValue("Size", new System.Drawing.Size(600,600));
SetFormPropertyValue("Size", new System.Drawing.Size(300,320));

SetFormPropertyValue 方法执行了所有工作(请参见图 6)。我使用 Object.GetType 方法创建表示应用程序窗体的 Type 对象,然后使用该对象获得引用窗体属性(例如 Location 属性或 Size 属性)的 PropertyInfo 对象。一旦拥有属性信息对象,就可以使用 PropertyInfo.SetProperty 方法来操作它。SetProperty 接受三个参数。前两个可能是您想要的 — 对包含要更改属性的对象的引用和对新属性值的引用。

第三个参数是必需的,因为有些属性(例如 Listbox 控件的 Items 属性)是索引的。我这里所做的移动窗体和改变窗体大小实际上与测试应用程序功能并不相关,但我想通过它告诉您如何实现以防您的测试方案需要。还要注意,我正在使用的是 Form 类(实际上是它的 Control 基类)公开的 ISynchronizeInvoke 接口。您应该只通过拥有控件底层窗口句柄的线程访问(包含 Form)控件的属性。对于待测试窗体,该线程就是为运行 Application.Run 而发起的线程。由于我的测试管理是在单独的线程中运行的,所以我需要将对控件的属性和方法的访问封送到该线程,使控件的 Invoke 方法和 InvokeRequired 属性成为一体(该方法和属性都是 ISynchronizeInvoke 接口的一部分)。有关 IsynchronizeInvoke 的更多信息,请参阅 MSDN Magazine 2003 年 2 月号中 Ian Griffith 的文章:Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads。

现在我准备模拟用户在文本框控件中键入一种颜色,并从 Combobox 控件选择一种颜色:

Console.WriteLine("\nSetting textBox1 to 'yellow'");
SetControlPropertyValue("textBox1", "Text", "yellow");
Console.WriteLine("Settin

上一个:C#异步数据接收串口操作类
下一个:.net下开发COM+组件

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,