漫话ID(下)——“自定义ID”以帮助定位自定义控件(实例和应用)

时间:2022-08-30 09:16:05

  在前面的两篇文章《漫话ID(上)——NameID的种种 和《漫话ID(中)——UniqueIDClientID中,我们讨论了关于ID的一些内容。在这一篇中,我想讨论一下关于自定义ID”的话题。

这个所谓的“自定义ID”跟前面提到的nameIDUniqueIDClientID没多大关系,但是借用了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属性应用到了TextBoxButton两种控件上。

步骤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~");

    }

运行结果:

漫话ID(下)——“自定义ID”以帮助定位自定义控件(实例和应用)

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跟控件的位置关联很大,这个时候即使我们有了良好的代码习惯——给这个自定义控件中的元素赋上一个诸如usernameTextid值,可是到了页面上它前面却加上了ctl00_holderMainContent_这些你无法控制的东西,一旦这个TextBox位置变动,它的id也立马变了样子。这个时候如果我们加上了这个testid,它的值依然是usernameText,这就让id在我们的控制之下了。

    说到这里,想必大家又发现了示例应用的另外一个缺陷,无法应用于DataGrid之类的控件中的子控件,使用示例中的方法生成的子控件的testid属性将是清一色的Button1

2)         举一反三

这才是我这篇文章的目的,我给大家介绍的是自定义test属性,仅仅只是重写了ControlAdapter的一个方法,仅仅只是借用了一下控件的ID,而大家在应用的时候则可以重写其他的方法,改写其他的部分,也可以自己定义一套属性的取之规则,总之,我只是举一,希望大家可以反三,而不仅仅局限在我的思路之中。

示例文件下载

链接地址:Simple.zip

示例文件中包括了自定义控件,DataGrid相关的代码,示例代码中不仅仅包括了本篇中讲到的自定义testid属性,大家也可以看到前两篇文章中讲到的UniqueIDClientID相关的一些示例。

示例在Aaron的机器上运行成功,考虑到大多数人用的还是VS2005,所以Aaron专门在VS2005环境中修改了相关的代码。详细运行环境:

VS2005

Windows XP SP2

IE 8 (由于笔者使用的WatiN版本问题,需要将IE设置为默认浏览器才可以成功运行WatiN代码示例。)

另外需要提醒读者注意的是:

    Selenium脚本需要先运行Selenium-server才可以运行成功。步骤参见Selenium IDE实践(使用Selenium录制)