博客文章都转移的 http://www.gh-m.cz.cc 上面了,请去新地址查看所有文章^o^
前段时间看了一下 生生不息原创 的《自己动手写web自动化测试框架》,感觉很不错,很值得学习一下,可是中间忙了,就暂停了,这几天清闲下,继续研究。终于走了一个遍,领悟了一下其中的原理,感觉对于简单的WEB UI测试还是很不错的,方便又便捷,嘿嘿,等下来根据这个编一个适合自己的winform版的,加到我的 要你命3000 中,恩恩!!
下面贴上 生生不息 的原创,以备收藏。不知道为什么 生生不息 没有下文了,怎么息了呢,想继续学习也没办法,只能借他的肩膀,自己学习了。
------------------------------------------------------------分割线---------------------------------------------------------------
28六2008 Filed under: 网海摘抄, 软件测试 Author: LeandRo
1 - 概述
记得几年前一本《自己动手写操作系统》在全国的技术范围内引起了学习操作系统的热潮。我不才在这里使用这本书的大名,来分享一下我在写Web自动化测试框架上面的一些经验。
首先定义一下Web自动化测试框架:Web自动化测试框架是一个类库,他可以帮助测试人员快速写出Web自动化测试代码,并帮助测试人员在自动化报错的时候快速找到Bug。
目前市场上成熟的Web自动化测试框架有不少,很出名的是Watir,是用现在大名鼎鼎的Ruby写成的,相对于Watir,有一个也很不错的.net版本,就是WatiN,WatiN可以说是市面上可以找到的最好的C# Web自动化测试框架。微软和其他的大公司也有自己的一些Web自动化测试框架,但是公布的并不多。
我这里将会使用C#,一步一步的写出一个最简单不过的自动化测试框架。这个框架的功能是基本可以测试简单的没有Ajax,没有框架,没有Windows对话框的一些网页。而如何测试Ajax之类的网页,我将会在其他的专题中写出。
首先我们看使用的非托管的类库。我们在这里将会使用两个非托管类库:mshtml.dll和Interop.SHDocVw.dll。 mshtml是微软IE的核心类库,下面是Wiki百科的解释:
Trident (also known as MSHTML) is the name of the layout engine for the Microsoft Windows version of Internet Explorer. It was first introduced with the release of Internet Explorer version 4 in October 1997, has been steadily upgraded and remains in use today. For version 7 of Internet Explorer, Microsoft made significant changes to the Trident layout engine to improve compliance with web standards and add support for new technologies.
开发人员可以通过mshtml提供的接口,访问到IE布局对象,从而达到对Web的控制和检查。
另一个类库Interop.SHDocVw.dll则提供了一个InternetExploer的接口,可以帮助我们操纵IE进程,并且进行一些简单的如前进,后退等操作。
这两个类库如果装了VS2005都可以找到。mshtml是IE自带的,在项目中选择添加引用,然后在.net标签下面找到Microsoft.mshtml就可以找到了。而Interop.SHDocVw要复杂一点,添加引用中,选择浏览标签,然后在下面的路径就可以找到:C:\Program Files\Microsoft Visual Studio 8\Application\PreEmptive Solutions\Dotfuscator Community Edition
下一次我们将会讲解如何创建IE的实例,并且操纵IE。
2 - 打开和操纵IE
这一个部分我们来讲用SHDocVw对IE进行操作。
接下来的几篇文章我们都会以Console Application来向大家介绍Web自动化的一些基础。
Notice 以下的代码在VS2005上通过测试,相信在VS2005 express已经VS2008上也可以通过,不过在VS2003上可能要稍微修改。使用vs2003的朋友,建议大家使用VS2005 express
浏览器使用了IE7。 IE6以及更低版本并没有做过试验。
首先我们打开VS2005,建立一个Console Application项目:
新建一个命令行工程
接下来我们需要包含两个引用了,就是mshtml和SHDocVw。关于这两个dll的简单介绍,请参见:自己动手写Web自动化测试框架1 - 概述
包含mshtml引用
第二个SHDocVw一定要在下面这个路径找:(C:\Program Files\Microsoft Visual Studio 8\Application\PreEmptive Solutions\Dotfuscator Community Edition)
包含SHDocVw
包含两个类库之后,我们就可以使用C#来对IE进行一些基本的操作了。
我们要添加几个命名空间,来简化我们下面的代码:
using System.Diagnostics; //要用到进程来启动IE窗口
using System.Threading; //使用Thread.Sleep来等待
using SHDocVw;
using mshtml;
除了mshtml和SHDocVw之外,我们还使用了其他的两个命名空间,因为我们要使用System.Diagnostics.Process类启动IE进程,并且获取IE的进程信息,使用System.Threading.Thread的sleep()方法等待。
接下来,我们就可以写入代码了:
Console.WriteLine("Launching IE ...");
Process p = Process.Start("iexplore.exe","about:blank");
Thread.Sleep(3000);
第一步,我们要打开IE进程,这里使用了Process的Start静态方法生成一个进程。很好理解,传入了两个参数,一个是IE的exe文件名,也可以写入完整地址;第二个参数是IE自己的参数,表示要打开的链接地址,我们在这里使用一个空白页面。
接下来的事情就是等待,等待IE进程启动,这里为了让大家更快的抓到本质,没有使用很复杂的等待代码,只是很机械的等待了3秒钟,大家可以根据自己机器的状况进行修改。
Notice 这里为了让大家可以更好的理解,我插一点Process的讲解,如果大家对上面的Process打开没有任何问题的话,直接跳过往下就好了。这里Process.Start()方法其实有4个重载,我们使用了第三个重载函数,也就是第一个输入文件名,第二个输入参数,我们可以在运行命令行中打"iexplore about:blank"直接打开一个空的IE窗口,也可以打"iexplore http://www.colblog.net/"直接打开一个浏览到目标网站的IE窗口。
IE启动了,我们接下来的事情就是把IE附加给SHDocVw.InternetExlporer以便我们可以进行接下来的操作。
Console.WriteLine("Attaching to IE ...");
InternetExplorer ie = null;
if (p != null)
{
SHDocVw.ShellWindows allBrowser = new ShellWindows();
if (allBrowser.Count != 0)
{
for (int i = 0; i < allBrowser.Count; i++)
{
InternetExplorer e = (InternetExplorer)allBrowser.Item(i);
if (e.HWND == (int)p.MainWindowHandle)
{
ie = e;
break;
}
}
}
}
else
{
Console.WriteLine("Error: Can not Launch IE");
return;
}
if (ie == null)
{
Console.WriteLine("Error: Can not attach to IE");
return;
}
首先,我们先声明了一个InternetExplorer的引用,这里设为空引用。他以后会存储我们的IE实例,并且通过它,我们可以操纵IE进行一些操作。
然后我们判断刚刚启动的进程p是不是为空,如果为空,就报错,说明不能启动IE(Can not Launch IE)。如果不为空,就可以进行下面的操作了。
注意,这里我们用了比较特殊的方法来得到InternetExplorer实例,就是通过比较当前Windows中已经存在的进程编号,来找到刚刚启动的IE实例,用allBrowser来获取所有的Shell Windows,然后逐个和刚刚启动的进程p的Handle号进行比较,如果相同,就说明是我们想要的进程,把他的InternetExplorer实例赋给我们ie。
最后我们判断一下ie引用是否有了实例,因为刚刚的循环我们无法保证一定有实例被赋给ie,这里如果检查失败,就报错返回。
通过上面的几步,我们已经启动并且拿到了InternetExplorer的实例,我们可以对这个实例进行一些操作了。举个最简单的例子就是浏览到某一个网页,下面的代码让这个浏览器指向了我的博客地址:
Console.WriteLine("Navigating ...");
object o = null;
ie.Navigate("www.colblog.net", ref o, ref o, ref o, ref o);
这里都很简单,只有一个让人费解的地方就是object o。因为ie.Navigate需要5个参数,后面的4个是可选参数,所以我们只需要象征性的传入一个null就可以了,有一个同事问我为啥不直接传null,还要申请一个object,其实原因就在于参数是ref的,必须有一个引用被传进去才行。关于后面的四个参数的讲解请看msdn上的讲解。
运行一下就发现,程序顺利的打开一个IE窗口,并且链接到我的博客上
下面附上源代码,不过需要登录才可以拿到,各位多多包含。
下一次我们来了解得到网页中的各个Element。
3 - 操纵Web控件
上面的两次课程我们介绍了mshtml和SHDocVw的一些用途,以及如何打开并且附加到IE上,实现IE的宏观上的控制。
这次我们将会用代码找到我们想要的控件,然后对控件进行一些操作。
首先我们引入一个很好的IE控件:Internet Explorer Developer Toolbar,这个控件可以帮助我们方便的找到我们想要的控件的属性。
安装好这个控件之后,我们就可以方便的找到每一个控件的ID,或者其他属性了,如下图
IE Developer
注意,打开IE Developer Toolbar之后,要点选下面的鼠标按钮,才可以用鼠标来选择我们想要的控件。有了这个控件,我们就不用去查看源文件来找到我们想要的信息了。其他的功能这里不多说了。
接下来我们以百度的三个控件为例,分别告诉大家如何使用ID得到TextBox,如何点击使用ID得到的Button,如何使用子控件缩小范围的方法得到一个HyperLink。
首先我们修改上次的代码,把IE指到百度去:
Console.WriteLine("Navigating ...");
object o = null;
ie.Navigate("baidu.com", ref o, ref o, ref o, ref o);
Thread.Sleep(2000);
代码我们在 《【生生不息原创】自己动手写Web自动化测试框架2 - 打开和操纵IE》 都讲解过了。只有一点,我们在完成IE的跳转之后,等待了2秒钟的时间,原因是IE的工作是需要时间的,我们在后面的测试框架部分会讲解如何判断IE已经完成了页面的跳转,在这里为了让大家更好的了解我们本节的主题,只是用了简单的等待。
然后我们用IE Developer Tools得到了关键字文本框的ID是kw,所以我们用下面的代码在关键字文本框里面输入了我们想要的关键字:
//得到一个Text Box
Console.WriteLine("Inputing Keyword ...");
HTMLDocument doc = (HTMLDocument)ie.Document;
HTMLInputElement keyword = (HTMLInputElement)doc.getElementById("kw");
keyword.value = "colblog.net";
Thread.Sleep(1000);
首先我们用ie.Document对象得到了HTMLDocument。目的没什么可说的,因为我们需要HTMLDocument得到下面的控件。而这里之所以使用强制类型转换,是因为Document对象在这里返回一个object的引用,但其实是一个HTMLDocument的实例。所以转换一下就好了,在mshtml里面,这种情况还不少,在msdn上有详细的讲解,使用的时候查一下就好了。
然后使用HTMLDocument.getElementById方法,直接从Document里面按照ID取出想要的控件,返回一个IHTMLElement,IHTMLElement是HTMLElement的抽象,所有的HTML的tag都可以是一个IHTMLElement,返回这样的一个引用,我们在知道将会返回什么类型的情况下,可以使用强制类型转换来把对象转成我们想要的引用。就像上面我们所做的,返回的其实是一个Input tag,所以我们要把他转换成HTMLInputElement就好了。
下面一句我们直接对这个对象的value进行设置,就可以完成在关键词文本框里面输入我们想要的关键词的动作。
接下来我们要点击搜索按钮:
//得到一个按钮
Console.WriteLine("Clicking Submit ...");
HTMLInputElement submit = (HTMLInputElement)doc.getElementById("sb");
submit.click();
Thread.Sleep(2000);
有了上面文本框的解释,这一段代码就容易多了吧。这里不在赘述。
聪明的读者一定会问:我们现在使用ID查询控件,如果我们的控件没有ID怎么办?如果ID是重复的怎么办?
上面的两种情况都是完全可能的,而且在实际中几乎占据了大部分的情况。(不过ASP.NET里面的控件倒是都有ID,使用这种方法比较方便。)我们下面的例子就是去点击百度首页右上角的登录超级链接。
首先我们分析一下,登录超级链接是放在一个id为u的div里面,而登录超级链接是没有ID的。我们的思路就是先找到这个id为u的div,然后找他的chidren找到我们想要的这个超级链接,下面是源代码:
//得到一个链接
Console.WriteLine("Clicking Login Button ...");
IHTMLElement userPanel = doc.getElementById("u");
IHTMLElementCollection HyperLinks = ((IHTMLElement2)userPanel).getElementsByTagName("a");
IHTMLElement login = (IHTMLElement)HyperLinks.item(null, 0);
login.click();
首先我们得到了那个id为u的div,命名为userPanel。这一步和上面没啥区别。
下面一个语句我们得到了userPanel的控件的所有tag为a的控件,也就是所有的超级链接。这里有一个小小的需要注意的地方,我们看到这个语句吧IHTMLElement对象强制类型转换成了IHTMLElement2,很有意思,为啥会这样呢?其实IHTMLElement有4个这样的兄弟,他们之间的方法不同,可以互相转换,我们想要的getElementsByTagName在IHTMLElement2下面,所以我们就强制类型转换到IHTMLElement2。这个方法返回一个IHTMLCollection。我们用HyperLinks来储存这个引用。
因为userPanel的子控件只有登录超级链接这一个,所以我们直接使用index为0来取道这个对象就好了。IHTMLElementCollection里面的item方法详见msdn,我们只需要把第二个index设置为0,就可以取到第一个子对象。下面附上源代码。
4 - 验证Web控件
上次我们讲到了如何控制Web控件,有了上次的基础,我们这一次的东西就会比较简单:如何验证Web控。
我们知道我们测试的目的就是判断被测产品是不是符合要求,如果是手动的测试,就是点到我们要判断的地方,然后用眼睛去判断出现的东西是不是我们想要的。
而自动化就稍微复杂一点。我们需要解决两大问题:一个是操纵电脑去点击,另一个就是去检查是不是我们想要的结果。我们前面的文章可以解决操纵浏览器进行浏览,而接下来我们讲的就是如何判断是否正确了。
不考虑性能测试的话,检查点无非有这么几个:
内容是否正确
样式是否正确
下面我们以实际代码来讲解如何判断。
首先判断一下样式,我们接着上次的代码,判断一下百度首页的文本框的大小是不是我们想要的(这个代码接着上次的得到keyword控件之后,Submit之前):
//验证
if (keyword.getAttribute("size", 0).ToString().Equals("36"))
Console.WriteLine("Validation Passed! Size is Correct");
else
Console.WriteLine("Validation Failed! Size is wrong");
我们可以看到IHTMLElement有getAttribute方法,这个方法可以得到一个Element里面的Attribute,比如value,type,以及css样式之类的,都可以用这个方法得到,见msdn对这个方法的更详细的讲解。
我们用下面的代码对百度的搜索结果的内容和标题进行判断:
//验证
if(string.Equals(doc.title.Trim(),"百度搜索_colblog.net"))
Console.WriteLine("Validation Passed! Title is Corrected");
else
Console.WriteLine("Validation Failed! Title is wrong");
if (doc.body.innerText.Contains("生生不息"))
Console.WriteLine("Validation Passed! Body contains your string");
else
Console.WriteLine("Validation Failed! Body do not contain");
IE的标题就直接用HtmlDocument的title属性就好,而判断字符就是用到了IHTMLElement的innerText属性,这个属性是我们很常用的属性之一,他会将该Element中显示在IE的字符串返回给我们,另一个相对应的属性就是InnerHtml属性,这个属性会把这个Element里的所有html代码返回。
比如说一个简单的Dropdownlist,用InnerText就会返回这个List里面每一项的text,而使用InnerHtml就会返回这个list里面的html代码。
在这里我们使用innerText来判断是否有我们想要的文字在里面。
Notice 注意:innerText和innerHtml属性,是IHTMLELement的属性,所以我们得到的每一个IHTMLElement都可以拿到这样的属性,并不是只有body才可以。这样我们以后就可以用前面的知识得到想要的IHTMLElement元素,然后再使用InnerHtml和InnerText来判断内容。
除了这两个,还有outerHtml和outerText可以使用。
这次的内容比较简单。有的网友问到了一些比较实际的问题,比如如何判断IE是否完成了页面读取,这里预告一下,在我的下一篇博客里,就会提到这个问题。敬请关注。
5 - 判断浏览器是否加载完成
上面的几次课程中,我们介绍了如何打开浏览器,如何获取每个web控件的信息,并且控制并验证他们。
从上面的文章中,我相信大家已经可以写出简单的测试程序了。但是还有一个很重要的问题没有解决:如何判断浏览器是否加载完成?
前面的文章我们没有对浏览器的加载进行判断,而只是简简单单的等一段时间,这不是一个很好的解决方法,一方面浪费了时间,另一方面,我们也无法知道应该等多久,导致我们的测试程序不够稳定。
接下来我们假设被测网页没有Ajax和框架,以这种情况来分析如何判断网页加载完毕。
现在比较常用判断是否加载的方法有三种:
不停判断IE的状态,如果没有准备好就等待。
实现IE的DocumentComplete事件,标志完成。
不停去查找页面有没有我们想要控件,没有就等待。
第一种方法:不停判断IE的状态,我们要判断IE的哪些状态呢?
一方面,我们需要判断IE的Busy状态,看IE是不是在忙着解析东西,另一方面判断IE的ReadyState状态,看html文档是不是被完全加载进来。
while (ie.Busy || ie.ReadyState != tagREADYSTATE.READYSTATE_COMPLETE)
{
Thread.Sleep(100);
}
用如上的代码就可以等待IE到完成。
Notice 这里只是简简单单的Demo,所以用了很简单的预计进行判断,我们假设我们的网页没有Ajax,也不会出现Load的死锁,真正的实际工作要比这个复杂一些,比如要定一个Time out,如果除了Timeout的范围,就强行终止,以防止测试过程中的死锁。
而如何判断Ajax是否被加载完,不是我们这个系列的讨论范围,请关注以后的其他系列文章。
这种方法是我比较推荐的一种方法,虽然《.net软件测试自动化之道》推荐的是第二种方法,不过我经过实际的测试,推荐第一种方法。这个方法可以比较好的处理Navigate、Submit等情况,也是WatiN使用的方法(WatiN的用法要复杂很多,考虑到了Frame等其他情况)。
第二种方法:通过绑定DocumentComplete,用AutoResetEvent来等待。
InternetExplorer给我们提供了DocumentComplete事件,会在IE被Load之后被调用,我们可以使用这个来等待。等待方法就是使用System.Threading.AutoResetEvent对象来。
所以我们需要做的是:
声明一个AutoResetEvent对象的实例,因为要在两个方法直接调用,所以需要放到类的成员变量。
在InternatExplorer被获取之后,绑定DocumentComplete事件。
在DocumentComplete事件中,调用AutoResetEvent.set()方法。
在等待页面加载的时候调用AutoResetEvent.WaitOne()方法
下面是源代码:
//1.声明AutoResetEvent对象实例
private static AutoResetEvent DocComplete = new AutoResetEvent(false);
static void Main(string[] args)
{
//...省略得到IE对象
//2.绑定DocumentComplete事件
ie.DocumentComplete +=
new DWebBrowserEvents2_DocumentCompleteEventHandler(ie_DocumentComplete);
Console.WriteLine("Navigating ...");
object o = null;
ie.Navigate("http://www.baidu.com/", ref o, ref o, ref o, ref o);
//调用WaitOne等待
DocComplete.WaitOne();
HTMLDocument doc = (HTMLDocument)ie.Document;
HTMLInputElement keyword = (HTMLInputElement)doc.getElementById("kw");
keyword.value = "colblog.net";
HTMLButtonElement submit = (HTMLButtonElement)doc.getElementById("sb");
submit.click();
//调用WaitOne等待
DocComplete.WaitOne();
ie.Quit();
}
//实现DocumentComplete事件,调用Set方法。
static void ie_DocumentComplete(object pDisp, ref object URL)
{
DocComplete.Set();
}
省略了一些前几篇文章的东西,完整代码请下载源代码。
这种方法有自己的好处,就是使用了IE自己的事件,判断代码很简单,不过有也不好的地方,第一就是帮度DocumentComplete事件以后,打开的IE会变得响应很慢,尤其是当设置断点调试的时候,IE会变得尤其的慢。第二个缺点就是如果加上框架,还有页面的跳转,就会是这个方法很难捉摸。比如,如果上面的例子里面,URL写成"baidu.cn",就会出错。
第三种方法是不停的去读我们要的控件是否出来。这个方法一般需要配合前面两个使用,而且也可以部分解决Ajax的问题。因为具体实现代码比较多,而又不是我们要讲的重点,就不贴出源代码,只是讲一下实现的思路,让大家了解一下。
具体实践方法是:设置一个Timeout,在这个Timeout时间之前,不停的去看我们要验证的控件是不是被Load进来。如果对页面进行建模,就会去看我们的这个页面的所有的已经定义了的控件是不是被Load进来,如果出了Timeout,就会报错。
这个方法的优点是可以部分解决Ajax,但是缺点也就是实现比较复杂,而且如果报错,无法分清是因为页面没有这个控件,还是因为速度较慢,控件还没有被读入。
以上介绍了在没有Ajax和框架情况下,如何判断浏览器是否已经加载完成。从下一篇文章开始,我们就要进入正题,开始我们的自动化测试框架了。
6 - 自动化测试框架的规划
从今天开始,我们正式进入自动化测试框架的编写中。
首先我们先进行需求分析:我们到底要什么样的自动化测试框架?
正如第一篇《【生生不息原创】自己动手写Web自动化测试框架1 - 概述》中提到的,我们要做的是一个简单的自动化测试框架,没有Ajax,没有框架,没有Windows对话框,我们舍弃这些较为复杂的功能,目的就在于,我们想要把注意力集中在自动化测试框架的架构上,以后我们可以慢慢加入这些功能,但是第一次,我们不要。
这里规划一下,我们想要的自动化测试框架是什么样子的,那么要从我们的自动化测试说起了。自动化测试代码一般是在什么时候写的呢?在微软里,自动化测试代码应该和被测试的网站的代码同步开发,因为有了Spec(Specification),我们就可以根据Spec来测试用例,然后把我们认为重要的,必须经常重复的用例自动化起来。
但是问题在于,我们在没有网站的情况下,如何进行自动化测试的开发呢?我们面临的困难主要有以下的方面:
没有网站,就没有网页元素的ID之类的标识,没有办法按照上面的办法获取我们想要的网页元素。
网站建设初期,页面元素不稳定,一个小小的ID的变更就可以使我们的自动化代码变的无用。
即使是页面元素不变,一个小小的业务逻辑的改变,也可能会很大的影响到我们的自动化测试代码。
我们的自动化测试框架,一定要可以比较好的解决上面的问题。
我想很多的读者已经明白了,我们要做的就是把网页的元素和网站的业务逻辑分开,这样就可以比较好的解决这些问题。
我们最终的目标是在一个类里面去定义整个网站的架构,比如这个网页上有一个文本框,有几个按钮。就像下面的这段代码
public class Baidu
{
WebBrowser wb = new WebBrowser("www.baidu.com");
private Button submit;
private TextBox keyword;
public Button Submit
{
get{
if (submit == null)
{
submit = new Button(wb, "sb");
}
return submit;
}
}
public TextBox Keyword
{
get
{
if (keyword == null)
{
keyword = new TextBox(wb, "kw");
}
return keyword;
}
}
}
上面的代码,我们定义了两个属性,一个是Button Submit,另一个是Textbox Keyword。这两个属性定义了百度首页的两个最重要的元素,我们也可以定义更多的比如登录的HyperLink或者其他的一些元素,但是我们现在以这个为例子来定义。
Notice 这里的代码定义并不是最简单的,读者完全可以通过自己的努力对测试框架进行修改,把这个代码做到更简单,不过我们这里以这个代码为例,来讲述自动化测试框架的架构等比较高层的东西。我们可以以后来细化这里。
经过以上的定义,我们的业务逻辑代码就可以被简略到如下的语句:
Baidu b = new Baidu();
b.Keyword.Text = "生生不息";
b.Submit.Click();
这里我想很简单,就是我们打开一个百度的实例,然后输入生生不息,然后点搜索按钮。我们以后还可以更多的建模,把验证也放在里面。
怎么样?如果我们的的自动化测试框架可以达到这样的效果,我们就可以很好的解决上面提出的问题,当Web的开发还没有完全成型的时候,我们可以定义页面的元素,空着ID不填,然后把业务逻辑做好,一旦Web开发完成,我们只需要填补上网页元素的定义,自动化测试代码就可以完成。
是不是已经摩拳擦掌了?我们从下一节开始,自动动手来做出这样一个自动化的测试框架来。
7 - 框架架构的设计
上次我们分析了什么是我们想要的自动化测试框架,这一次我们基于上一次的分析,来设计我们的自动化测试框架架构。
首先,我们需要一个类,去做帮助我们处理和IE相关的事情,帮助我们操纵IE,打开和关闭IE。然后,我们需要一组的类,来描述网页上的元素,比如TextBox和Button,去模拟他们的事件,去取得他们的状态。总体来说就是封装这些元素。
除此之外,我们还需要一些辅助的类来帮助我们做一些工作。
接下来我们就先去分析帮助我们操纵IE的类应该有那些功能。所以我们把这个类命名为WebBrowser。参考附带的WebBrowser.cs文件。
首先,我们需要一些内部的成员和辅助类,我们打开的IE进程会用SHDocVw.InternetExplorer类来接受,我们可以把这个变量存在整个类的私有变量去。还有我们其实要取出的是这个网页的HTMLDocument类,以供以后来使用。这里我们直接写成了internal,成员变量,其实可以封装成一个属性,看大家的用法了。除了这些,我们还需要一个Timer类,这个类帮助我们完成一些计时的工作。见附带的timer.cs文件。
其次,打开IE进程这样的事情应该是由这个类来做的,由于这次对WebBrowser的要求不高,我就在构造函数里面讲IE进程初始化好,具体如何处理这些请参见《【生生不息原创】自己动手写Web自动化测试框架2 - 打开和操纵IE》。我们给这个类构造函数提供了一个带有string参数的方法,目的是让用户可以方便的传入URL而打开浏览器并且转到我们要的页面去。当我们没有参数给构造函数时,用户就会得到一个空的页面。
再次,我们需要一个NavigateTo函数,来操纵浏览器转到某一个地方去。这个函数不多说,就是我们以前的内容。
最后,我们在这里添加了一个DoWait的函数,这个函数的主要目的是完成我们《【生生不息原创】自己动手写Web自动化测试框架5 - 判断浏览器是否加载完成》里面所讨论的等待浏览器加载完成。DoWait放在这里的原因是我们需要在几个地方判断页面加载完成:浏览器浏览到了新的页面、用户点击了某些按钮或者链接、或者甚至用户改变了控件的状态,导致页面回刷,这几个地方会分别被WebBrowser和以后的Web控件类所使用,所以我们把这个DoWait放在WebBrowser里面,以后的Web控件回去引用WebBrowser类的这个方法来判断浏览器是否完成加载。
Notice 注意,这里把DoWait的代码写在WebBrowser里面是基于我们以上的页面没有Ajax和框架的假设做出的,如果页面可能有Ajax,我们就不可能简简单单的用这个DoWait去等待加载完成。
我们以后会有对目前框架的改进文章,这些文章会讨论这些问题。
这样我们的WebBrowser就大概写完了。下一次我们会讲解一组类来实现Web控件。
------------------------------------------------------------分割线---------------------------------------------------------------
下面贴上我测试用的源代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using SHDocVw;
using mshtml;
namespace kj01
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Launching IE ...");
Process p = Process.Start("iexplore.exe", "about:blank");
Thread.Sleep(3000);
Console.WriteLine("Attaching to IE ...");
InternetExplorer ie = null;
if (p != null)
{
SHDocVw.ShellWindows allBrowser = new ShellWindows();
if (allBrowser.Count != 0)
{
for (int i = 0; i < allBrowser.Count; i++)
{
InternetExplorer e = (InternetExplorer)allBrowser.Item(i);
if (e.HWND == (int)p.MainWindowHandle)
{
ie = e;
break;
}
}
}
}
else
{
Console.WriteLine("Error: Can not Launch IE");
return;
}
if (ie == null)
{
Console.WriteLine("Error: Can not attach to IE");
return;
}
Console.WriteLine("Navigating ...");
object o = null;
ie.Navigate("www.baidu.com", ref o, ref o, ref o, ref o);
Thread.Sleep(2000);
//得到一个Text Box
Console.WriteLine("Inputing Keyword ...");
HTMLDocument doc = (HTMLDocument)ie.Document;
HTMLInputElement keyword = (HTMLInputElement)doc.getElementById("kw");
keyword.value = "天津";
Thread.Sleep(1000);
//验证
if (keyword.getAttribute("size", 0).ToString().Equals("36"))
Console.WriteLine("Validation Passed! Size is Correct");
else
Console.WriteLine("Validation Failed! Size is wrong");
//得到一个按钮
Console.WriteLine("Clicking Submit ...");
HTMLInputElement submit = (HTMLInputElement)doc.getElementById("sb");
submit.click();
Thread.Sleep(2000);
//验证
if (string.Equals(doc.title.Trim(), "百度搜索_天津"))
{
Console.WriteLine("Validation Passed! Title is Corrected");
//Console.ReadLine;
}
else
Console.WriteLine("Validation Failed! Title is wrong");
if (doc.body.innerText.Contains("天津地图"))
{
Console.WriteLine("Validation Passed! Body contains your string");
Console.ReadLine();
}
else
Console.WriteLine("Validation Failed! Body do not contain");
////得到一个链接
//Console.WriteLine("Clicking Login Button ...");
//IHTMLElement userPanel = doc.getElementById("u");
//IHTMLElementCollection HyperLinks = ((IHTMLElement2)userPanel).getElementsByTagName("a");
//IHTMLElement login = (IHTMLElement)HyperLinks.item(null, 0);
//login.click();
}
}
}