一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  回顾 

  在之前的两章分别介绍了一个简单的示例操作动作的录制原理通过修改UIMap.UItest文件控制操作动作代码的生成。想必大家对编码的UI测试操作动作的录制应该有一定了解了,在UI操作中,操作动作总是离不开具体的操作对象,例如点击,拖动,填写值等操作,总是要对文本框,或者按钮,窗体这些对象进行操作,不是凭空生成的。本章作为入门篇的最后一章,将会说明编码的UI测试是如何识别这些对象的,并介绍UIMap.UItest文件是如何生成对象识别代码。

  对象的识别原理

  之前我们录制的时候,录制生成器解析了我们的操作对象,并且为我们生成了对象识别代码(也就是UIMap.UItest下<Map>节点的那部分),以便我们在回放的时候,能够识别到我们要操作的是哪一个对象。

  在编码的UI测试中,对象的识别主要是同时按以下三个方面进行。

  1. 按对象类别:对象属于什么类别,例如是button还是textBox 。

  2. 按关键标识属性:能唯一识别该对象的属性 。

  3. 按层级关系:对象在其父对象中的层次 。

  下面开始进行详细介绍: 

  一、按对象类别:  

  为便于理解,以web程序为例,假设我们的识别代码中,写明让VS2010按WebButton识别一个对象,那么VS2010则会在页面源码中只找到那些html标签是<input type=“submit”>的控件,如果识别代码中描写的是按WebLink识别对象,则VS2010只在页面源码中找到那些是<a>的标签。 

一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  二、按关键标识属性:

  通过对象类别,已经能识别出来某一类的对象,在这个基础上,接下来就需要用关键识别属性进行精准标识了,我们需要找出能唯一识别该对象的属性,假如一个窗体如下。

一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  这个窗体中包含了3个button,我们想让VS2010操作到第一个Button,如何区分这些Button呢?这就要用到关键标识属性来标识唯一一个对象。

  关键标识属性分为两种----自身标识属性和辅助标识属性。 

  1、自身标识属性: 

  也就是属于对象的属性,例如,button拥有Name,Text,Enable,BackColor等属性,我们假设上面那个窗体的Button的各个属性如下。

一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  很明显可以发现,第一个button的Name属性和和其他的button不一样,那么可以用其作为他的关键标识属性,并且写在识别代码中,这样vs2010就可以在这些button中能唯一识别他,例如,将Name=button2写到识别代码中,就可以唯一识别第二个Button。

  假设3个button的Name一样,而BackColor不同,也可以用BackColor作为关键标识属性。

  2、辅助标识属性:

  在自动化测试中,还有一种属性叫做辅助标识属性,他并不属于对象的属性,但可以辅助标识对象。

  假设刚才那个窗体的各button属性如下:

一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  可以发现他们的自身属性都是一模一样的,那么怎么做才能让VS2010识别他呢?仔细观察可以发现这样一个情况。

一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  我们可以通过该对象是第几个对象这样的辅助属性进行标识他。

  比如如果要识别第3个Button,则需要在识别代码中设置其辅助标识属性“对象序号”为3。

  三、按层级关系:

  虽然上面确定了唯一对象,的确已经可以标识出对象了,不过实际上在识别的时候还会按层级关系查找,例如第一章提到的一个简单的窗体。

一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  我们用录制生成器解析他的第一个文本框Tbx_uid,在自动生成的识别代码中,他们的层级关系是这样的。

一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  对应到刚才的Form,层级为:

一起谈.NET技术,VS2010测试功能之旅:编码的UI测试(3)-操作对象的识别原理

  VS2010在识别这个Textbox1的时候,会只识别属于 系统登录Window下的Tbx_uidWindow下的Tbx_uid文本框。

  (注:按照常规的理解,其实应该“系统登录窗体”和“Tbx_uid文本框”只有两个层级,也就是窗体下直接就是文本框,但是VS2010默认解析的时候,会把一个TextBox解析成两个层级,分为TextBoxWindow和TextBoxEdit,结果本来应该总共只有两级结果搞成了三级,如果仔细观察可以发现其他控件也是这样的,所以在这里纠结的朋友希望你们看到此段后能够减轻些疑惑...)

  UIMap.UItest文件控制识别代码的生成

  在第二章上部分已经提到,在UIMap.uitest文件中,对象的识别代码的生成部分是放在<Maps>节点下的,点击生成代码后,会将其真正起作用的识别代码生成到UIMap.designer.cs下,那么他们的代码究竟是怎样的呢?

以刚才那个窗体生成的代码为例,我们要识别TextBox1,如果使用录制生成器,把TextBox1填入到对象库,我们来看看他生成的识别代码(可以简略看,下面会有说明)。


<Maps>
<UIMap Id="UIMap1">
<TopLevelWindows>
<TopLevelWindow ControlType="Window" Id="UI 系统登录Window" FriendlyName="系统登录" SpecialControlType="None" SessionId="199642">
<TechnologyName>MSAA</TechnologyName>
<WindowTitles>
<WindowTitle> 系统登录</WindowTitle>
</WindowTitles>
<SearchConfigurations>
<SearchConfiguration>VisibleOnly</SearchConfiguration>
</SearchConfigurations>
<AndCondition Id="SearchCondition">
<PropertyCondition Name="Name"& gt;系统登录</PropertyCondition>
<PropertyCondition Name="ClassName" Operator="Contains">WindowsForms10.Window</PropertyCondition>
<PropertyCondition Name="ControlType">Window</PropertyCondition>
</AndCondition>
<SupportLevel>0</SupportLevel>
<Descendants>
<UIObject ControlType="Window" Id="UITbx_uidWindow" FriendlyName="Tbx_uid" SpecialControlType="None">
<TechnologyName>MSAA</TechnologyName>
<WindowTitles>
<WindowTitle> 系统登录</WindowTitle>
</WindowTitles>
<SearchConfigurations>
<SearchConfiguration>VisibleOnly</SearchConfiguration>
</SearchConfigurations>
<AndCondition Id="SearchCondition">
<PropertyCondition Name="ControlName">Tbx_uid</PropertyCondition>
<PropertyCondition Name="ControlType">Window</PropertyCondition>
</AndCondition>
<SupportLevel>0</SupportLevel>
<Descendants>
<UIObject ControlType="Edit" Id="UITbx_uidEdit" FriendlyName="Tbx_uid" SpecialControlType="None">
<TechnologyName>MSAA</TechnologyName>
<WindowTitles>
<WindowTitle> 系统登录</WindowTitle>
</WindowTitles>
<SearchConfigurations>
<SearchConfiguration>VisibleOnly</SearchConfiguration>
</SearchConfigurations>
<AndCondition Id="SearchCondition">
<PropertyCondition Name="Name"& gt;密码 :</PropertyCondition>
<PropertyCondition Name="ControlType">Edit</PropertyCondition>
</AndCondition>
<SupportLevel>0</SupportLevel>
<Descendants />
</UIObject>
</Descendants>
</UIObject>
</Descendants>
</TopLevelWindow>
</TopLevelWindows>
</UIMap>
</Maps>

  别看UIMap.uitest里为这些对象生成的了一大堆代码,其实对象中真正管作用的属性就ControlType,ID两个:

  ID这个属性表示调用对象在对象库中的ID,例如在第二章下部分,编写ExecuteActions的时候,描述对那个对象进行操作

  ControlType这个属性用于描述按什么类别来识别对象,识别原理之一的对象类别就是用这个表示  

   而管作用的节点也就这么两个:

  <AndCondition>这个节点表示识别条件,他的子节点为<PropertyCondition>,表示具体识别条件,识别原理之二的关键标识属性就用这个表示

  <Descendants>这个节点用于存放这个对象的子对象,识别原理之三的层级关系就是用这个表示 。

  现在把上面自动生成的代码优化一下,把不必要的代码删除,并加上注释。然后再使用这段XML代码在录制生成器生成UIMap.designer.cs代码:


<Maps>
<UIMap Id="UIMap1">
<TopLevelWindows>
<!-- 表示*别的窗体,属性ControlType表示按类型“Window”类型识别对象,对应识别原理之一-->
<TopLevelWindow ControlType="Window" Id="UI 系统登录Window">
<!--TechnologyName标签没有实际作用,但是删除了会报错,因此保留,之后的也是-->
<TechnologyName>MSAA</TechnologyName>
<AndCondition Id="SearchCondition">
<!-- 表示窗体的关键标识属性,识别的时候寻找Name属性为“系统登录”的窗体,对应识别原理之二-->
<PropertyCondition Name="Name"& gt;系统登录</PropertyCondition>
<!--表示窗体的关键标识属性,识别的时候寻找ClassName属性为“WindowsForms10.Window”的窗体-->
<PropertyCondition Name="ClassName" Operator="Contains">WindowsForms10.Window</PropertyCondition>
</AndCondition>
<!-- 表示窗体的子对象,对应识别原理之三-->
<Descendants>
<!--UI 对象,按类型“Window”类型识别-->
<UIObject ControlType="Window" Id="UITbx_uidWindow">
<TechnologyName>MSAA</TechnologyName>
<AndCondition Id="SearchCondition">
<!-- 关键标识属性,ControlName属性为“Tbx_uid”的对象-->
<PropertyCondition Name="ControlName">Tbx_uid</PropertyCondition>
</AndCondition>
<!-- 他的子对象-->
<Descendants>
<!--UI 对象,按类型“Edit”(文本框)类型识别-->
<UIObject ControlType="Edit" Id="UITbx_uidEdit">
<TechnologyName>MSAA</TechnologyName>
<AndCondition Id="SearchCondition">
<!-- 关键标识属性,Name属性为“密码 :”-->
<PropertyCondition Name="Name"& gt;密码 :</PropertyCondition>
</AndCondition>
<Descendants />
</UIObject>
</Descendants>
</UIObject>
</Descendants>
</TopLevelWindow>
</TopLevelWindows>
</UIMap>
</Maps>

  接下来使用录制生成器生成UIMap.designer.cs代码,可以看到代码如下: 


[GeneratedCode("编码的 UI 测试生成器", "10.0.30319.1")]
public class UI 系统登录Window : WinWindow//这里的继承于WinWindow,是根据刚才XML文件里的属性 ControlType="Window"生成的,表示按Window识别,对应识别原理之一
{
public UI 系统登录Window()
{
//这里是搜索条件,也就是关键识别属性,根据XML 文件的AndCondition节点中的内容生成,对应识别原理之二
#region 搜索条件
this.SearchProperties[WinWindow.PropertyNames.Name] = " 系统登录";
this.SearchProperties.Add(new PropertyExpression(WinWindow.PropertyNames.ClassName, "WindowsForms10.Window", PropertyExpressionOperator.Contains));
#endregion
}

// 这里是他的子对象,对应于XML文件的Descendants节点,对应识别原理之三
#region Properties
public UITbx_uidWindow UITbx_uidWindow
{
get
{
if ((this.mUITbx_uidWindow == null))
{
this.mUITbx_uidWindow = new UITbx_uidWindow(this);
}
return this.mUITbx_uidWindow;
}
}
#endregion

#region Fields
private UITbx_uidWindow mUITbx_uidWindow;
#endregion
}
[GeneratedCode(
" 编码的 UI 测试生成器", "10.0.30319.1")]
public class UITbx_uidWindow : WinWindow// 这里的继承于WinWindow,是根据刚才XML文件里的属性ControlType="Window"生成的,表示按Window识别
{

public UITbx_uidWindow(UITestControl searchLimitContainer) :
base(searchLimitContainer)
{
// 这里是搜索条件,也就是关键识别属性,根据XML文件的AndCondition节点中的内容生成
#region 搜索条件
this.SearchProperties[WinWindow.PropertyNames.ControlName] = "Tbx_uid";
#endregion
}
// 这里是他的子对象,对应于XML文件的Descendants节点
#region Properties
public WinEdit UITbx_uidEdit// 这里的类型为WinEdit,是根据刚才XML文件里的属性ControlType生成的(ControlType="Edit")
{
get
{
if ((this.mUITbx_uidEdit == null))
{
this.mUITbx_uidEdit = new WinEdit(this);
// 这里是搜索条件,也就是关键识别属性,根据XML文件的AndCondition节点中的内容生成
#region 搜索条件
this.mUITbx_uidEdit.SearchProperties[WinEdit.PropertyNames.Name] = " 密码 :";
#endregion
}
return this.mUITbx_uidEdit;
}
}
#endregion

#region Fields
private WinEdit mUITbx_uidEdit;
#endregion
}

总结

  在本章介绍了VS2010的对象识别原理,想必大家对这个应该有所了解了吧?

  我曾今在用UI测试做项目的时候,经常会发现这样那样的对象不能用录制生成器识别,这个时候怎么办呢?了解了对象识别原理,就可以自己改XML文件来控制对象的识别,如果有想做这方面练习的朋友,可以留下你的邮箱,我这里正在编写一个C# Winform小程序,这个程序上的所有控件都经过特殊处理,无法用录制生成器识别,只有通过自己修改XML文件来控制它的识别,如果想试试自己的身手,别忘了留下邮箱哦~~

  到这里编码的UI测试的入门篇就完结了,接下来会进入进阶篇,以实际的例子描述如何对一个项目进行测试。

  附1:VS2010编码的UI测试支持识别的对象  

平台 支持级别
Windows Internet Explorer 7.0
Windows Internet Explorer 8.0
(包括 HTML 和 AJAX)
完全支持
Windows Internet Explorer 6.0 不支持
镶边OperaSafari 不支持
Silverlight 3.0 不支持
Flash/Java 不支持
Windows 窗体 2.0 及更高版本 完全支持
注意
完全支持 NetFx 控件,但并非支持所有第三方控件。
WPF 3.5 及更高版本 完全支持
注意
完全支持 NetFx 控件,但并非支持所有第三方控件。
Windows Win32 可适用于某些已知问题,但不正式支持
MFC 可适用于某些已知问题,但不正式支持
SharePoint 部分支持
Office 客户端应用程序 不支持
Dynamics (Ax) 部分支持
SAP 不支持
Citrix/终端服务 部分支持

  注:Silverlight4 现在已支持,详情见http://www.silverlightshow.net/news/Coded-UI-testing-for-Silverlight-4-now-available-.aspx

  园子里scottxu已有这方面研究,相关文章http://www.cnblogs.com/scottxu/archive/2011/02/28/1967112.html

[GeneratedCode("编码的 UI 测试生成器", "10.0.30319.1")]
public class UI 系统登录Window : WinWindow//这里的继承于WinWindow,是根据刚才XML文件里的属性 ControlType="Window"生成的,表示按Window识别,对应识别原理之一
 {

    public UI 系统登录Window()
    {
        //这里是搜索条件,也就是关键识别属性,根据XML 文件的AndCondition节点中的内容生成,对应识别原理之二
         #region 搜索条件
        this.SearchProperties[WinWindow.PropertyNames.Name] = " 系统登录";
        this.SearchProperties.Add(new PropertyExpression(WinWindow.PropertyNames.ClassName, "WindowsForms10.Window", PropertyExpressionOperator.Contains));
        #endregion
    }

    // 这里是他的子对象,对应于XML文件的Descendants节点,对应识别原理之三
     #region Properties
    public UITbx_uidWindow UITbx_uidWindow
    {
        get
        {
            if ((this.mUITbx_uidWindow == null))
            {
                this.mUITbx_uidWindow = new UITbx_uidWindow(this);
            }
            return this.mUITbx_uidWindow;
        }
    }
    #endregion

    #region Fields
    private UITbx_uidWindow mUITbx_uidWindow;
    #endregion
}


[GeneratedCode(" 编码的 UI 测试生成器", "10.0.30319.1")]
public class UITbx_uidWindow : WinWindow// 这里的继承于WinWindow,是根据刚才XML文件里的属性ControlType="Window"生成的,表示按Window识别
 {

    public UITbx_uidWindow(UITestControl searchLimitContainer) :
            base(searchLimitContainer)
    {
        // 这里是搜索条件,也就是关键识别属性,根据XML文件的AndCondition节点中的内容生成
         #region 搜索条件
        this.SearchProperties[WinWindow.PropertyNames.ControlName] = "Tbx_uid";
        #endregion
    }
    // 这里是他的子对象,对应于XML文件的Descendants节点
     #region Properties
    public WinEdit UITbx_uidEdit// 这里的类型为WinEdit,是根据刚才XML文件里的属性ControlType生成的(ControlType="Edit")
    {
        get
        {
            if ((this.mUITbx_uidEdit == null))
            {
                this.mUITbx_uidEdit = new WinEdit(this);
                // 这里是搜索条件,也就是关键识别属性,根据XML文件的AndCondition节点中的内容生成
                #region 搜索条件
                this.mUITbx_uidEdit.SearchProperties[WinEdit.PropertyNames.Name] = " 密码 :";
                #endregion
            }
            return this.mUITbx_uidEdit;
        }
    }
    #endregion

    #region Fields
    private WinEdit mUITbx_uidEdit;
    #endregion
}
上一篇:读取INI文件 ---VbCode


下一篇:如何构建一套高性能、高可用性、低成本的视频处理系统?