在前面的两篇文章《漫话ID(上)——Name和ID的种种》 和《漫话ID(中)——UniqueID和ClientID》中,我们讨论了关于ID的一些内容。在这一篇中,我想讨论一下关于“自定义ID”的话题。
这个所谓的“自定义ID”跟前面提到的name,ID,UniqueID,ClientID没多大关系,但是借用了id的特质——我们可以为控件添加一个自定义属性,暂时称之为testid,然后将id的值赋给testid。这可以应用于自定义控件较多的情形下的Web自动化测试之中,这样我们访问到的属性是一个有意义的可读的属性,而且不会因为自定义控件中的子控件的位置改变而导致生成的UniqueID改变造成的测试脚本失效。自然,这个方法也有它很大的局限性,具体在后面会提到,先还是介绍一下我的方法。
思路
重写System.Web.UI.Adapters.ControlAdapter中的BeginRender方法,将控件的ID值直接拿出来赋给一个新的属性testid,并利用浏览器配置文件并将这个testid属性显示到最终的页面中。
实现步骤
步骤1:重写System.Web.UI.Adapters.ControlAdapter中的BeginRender方法,将控件的ID值赋给新属性testid。
public class HtmlControlAdapter:System.Web.UI.Adapters.ControlAdapter
{
protected override void BeginRender(HtmlTextWriter writer)
{
writer.AddAttribute("testid", this.Control.ID);
base.BeginRender(writer);
}
}
步骤2:使用浏览器配置文件加入修改后的内容
<!--
可在 <windir>\Microsoft.NET\Framework\<ver>\CONFIG\Browsers 中找到现有的浏览器定义
-->
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.WebControls.TextBox"
adapterType="TestIDWebProject.HtmlControlAdapter" />
<adapter controlType="System.Web.UI.WebControls.Button"
adapterType="TestIDWebProject.HtmlControlAdapter" />
</controlAdapters>
</browser>
</browsers>
在配置文件中我们将testid属性应用到了TextBox和Button两种控件上。
步骤3:实验
在deafult.aspx页面中加入下面的内容:
<center>
<table>
<tr>
<td>
<asp:Button Text="This is a normal button" runat="server" ID="NormalButton" />
</td>
</tr>
<tr>
<td>
<asp:Label Text="" runat="server" ID="NormalLabel"></asp:Label>
</td>
</tr>
</table>
</center>
然后在浏览器中查看:
<center>
<table>
<tr>
<td>
<input testid="NormalButton" type="submit" name="NormalButton" value="This is a normal button" id="NormalButton" />
</td>
</tr>
<tr>
<td>
<span id="NormalLabel"></span>
</td>
</tr>
</table>
</center>
可以看到我们如愿以偿:Button控件已经成功添加了属性testid,而label控件则没有testid属性。
应用
在Web自动化测试之中,我们可以使用Selenium或者WatiN来通过这个自定义的testid找到我们的控件,并点击。
Selenium测试脚本:
static ISelenium selenium;
static string FindButtonByTestid1()
{
selenium = new DefaultSelenium("localhost", 4444, "*iexplore", "http://localhost:9847/TestDataGrid.aspx");
selenium.Start();
selenium.Open("http://localhost:9847/TestDataGrid.aspx");
/***************************
* 注意最后一个参数应该更改为你运行时候的URL
*******************************/
selenium.Click("xpath=//input[@testid='NormalButton']");
//如果上面的代码没有找到button,会抛出异常,如果没有抛出异常则证明找到了该button
return "Success";
}
在上面的脚本中我们使用了XPath来定位这个button,其中只利用了testid属性就可以找到,而不需要使用id('centercontent')/div[1]/table[1]/tbody/tr[2]/td[2]这种对于控件位置依赖性极其严重的定位方式。为了让结果更加明显,我在测试脚本中为button添加了一个button_click时间:
protected void NormalButton_Click(object sender, EventArgs e)
{
Response.Write("You clicked the normal button~");
}
运行结果:
WatiN测试脚本:
[STAThread]
static void Main1(string[] args)
{
Console.WriteLine("Now we try to get the button by its testid...");
browser = IE.AttachToIE(Find.ByTitle("TestDataGrid"));
Console.WriteLine("Using WatiN tool, please waiting...");
Console.WriteLine(FindButtonByTestid());
Console.ReadKey();
}
static IE browser;
static string FindButtonByTestid()
{
string buttonInfo = null;
if (null != browser && !String.IsNullOrEmpty(buttonInfo = Find.By("testid", "NormalButton").ToString()))
return buttonInfo;
else
return "Failed to get the button";
}
缺陷
相信看到这里的时候就已经有兄弟姐妹开始质疑我这样做的意义了,没必要为了自动化测试来做这么麻烦的配置,譬如应用Selenium的话我们可以通过除了ID的很多种方法来定位web上的元素,更何况我这种做法还依赖于控件原有的id,要求控件原有的Id是唯一的,以便我们可以使用这个testid来定位这个元素。
总结
确实,这个方法有很多缺陷,但是在我看来,自定义属性还是有一些应用意义的:
1) 在自定义控件相关的自动化测试之中使用
前面我们提到过,自定义控件的ID会变成诸如
原:usernameText
页面显示:ctl00_holderMainContent_usernameText
之类的形式,这个id跟控件的位置关联很大,这个时候即使我们有了良好的代码习惯——给这个自定义控件中的元素赋上一个诸如usernameText的id值,可是到了页面上它前面却加上了ctl00_holderMainContent_这些你无法控制的东西,一旦这个TextBox位置变动,它的id也立马变了样子。这个时候如果我们加上了这个testid,它的值依然是usernameText,这就让id在我们的控制之下了。
说到这里,想必大家又发现了示例应用的另外一个缺陷,无法应用于DataGrid之类的控件中的子控件,使用示例中的方法生成的子控件的testid属性将是清一色的Button1。
2) 举一反三
这才是我这篇文章的目的,我给大家介绍的是自定义test属性,仅仅只是重写了ControlAdapter的一个方法,仅仅只是借用了一下控件的ID,而大家在应用的时候则可以重写其他的方法,改写其他的部分,也可以自己定义一套属性的取之规则,总之,我只是举一,希望大家可以反三,而不仅仅局限在我的思路之中。
示例文件下载
链接地址:Simple.zip
示例文件中包括了自定义控件,DataGrid相关的代码,示例代码中不仅仅包括了本篇中讲到的自定义testid属性,大家也可以看到前两篇文章中讲到的UniqueID和ClientID相关的一些示例。
示例在Aaron的机器上运行成功,考虑到大多数人用的还是VS2005,所以Aaron专门在VS2005环境中修改了相关的代码。详细运行环境:
VS2005
Windows XP SP2
IE 8 (由于笔者使用的WatiN版本问题,需要将IE设置为默认浏览器才可以成功运行WatiN代码示例。)
另外需要提醒读者注意的是:
Selenium脚本需要先运行Selenium-server才可以运行成功。步骤参见Selenium IDE实践(使用Selenium录制)。