自己動手寫Web自動化測試框架

Just4life發表於2013-07-30

記得幾年前一本《自己動手寫作業系統》在全國的技術範圍內引起了學習作業系統的熱潮。我不才在這裡使用這本書的大名,來分享一下我在寫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

這一個部分我們來講用SHDocVw對IE進行操作。

接下來的幾篇文章我們都會以Console Application來向大家介紹Web自動化的一些基礎。

以下的程式碼在VS2005上通過測試,相信在VS2005 express已經VS2008上也可以通過,不過在VS2003上可能要稍微修改。使用vs2003的朋友,建議大家使用VS2005 express。

瀏覽器使用了IE7。 IE6以及更低版本並沒有做過試驗。

首先我們開啟VS2005,建立一個Console Application專案:

新建一個命令列工程

接下來我們需要包含兩個引用了,就是mshtml和SHDocVw。

包含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秒鐘,大家可以根據自己機器的狀況進行修改。

這裡為了讓大家可以更好的理解,我插一點Process的講解,如果大家對上面的Process開啟沒有任何問題的話,直接跳過往下就好了。這裡Process.Start()方法其實有4個過載,我們使用了第三個過載函式,也就是第一個輸入檔名,第二個輸入引數,我們可以在執行命令列中打"iexplore about:blank"直接開啟一個空的IE視窗,也可以打"iexplorehttp://www.colblog.net/"直接開啟一個瀏覽到目標網站的IE視窗

IE啟動了,我們接下來的事情就是把IE附加給SHDocVw.InternetExlporer以便我們可以進行接下來的操作。

上面的兩次課程我們介紹了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)

開啟和操縱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進行設定,就可以完成在關鍵詞文字框裡面輸入我們想要的關鍵詞的動作。

接下來我們要點選搜尋按鈕:

有了上面文字框的解釋,這一段程式碼就容易多了吧。這裡不在贅述。

聰明的讀者一定會問:我們現在使用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,就可以取到第一個子物件。

上次我們講到了如何控制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來判斷是否有我們想要的文字在裡面。

NoticeNotice

注意:innerText和innerHtml屬性,是IHTMLELement的屬性,所以我們得到的每一個IHTMLElement都可以拿到這樣的屬性,並不是只有body才可以。這樣我們以後就可以用前面的知識得到想要的IHTMLElement元素,然後再使用InnerHtml和 InnerText來判斷內容。

除了這兩個,還有outerHtml和outerText可以使用。

這次的內容比較簡單。下一篇將會提到如何判斷IE是否完成了頁面讀取的問題。

上面的幾次課程中,我們介紹瞭如何開啟瀏覽器,如何獲取每個web控制元件的資訊,並且控制並驗證他們。

從上面的文章中,我相信大家已經可以寫出簡單的測試程式了。但是還有一個很重要的問題沒有解決:如何判斷瀏覽器是否載入完成?

前面的文章我們沒有對瀏覽器的載入進行判斷,而只是簡簡單單的等一段時間,這不是一個很好的解決方法,一方面浪費了時間,另一方面,我們也無法知道應該等多久,導致我們的測試程式不夠穩定。

接下來我們假設被測網頁沒有Ajax和框架,以這種情況來分析如何判斷網頁載入完畢。

現在比較常用判斷是否載入的方法有三種:

1. 不停判斷IE的狀態,如果沒有準備好就等待。

2. 實現IE的DocumentComplete事件,標誌完成。

3. 不停去查詢頁面有沒有我們想要控制元件,沒有就等待。

第一種方法:不停判斷IE的狀態,我們要判斷IE的哪些狀態呢?

一方面,我們需要判斷IE的Busy狀態,看IE是不是在忙著解析東西,另一方面判斷IE的ReadyState狀態,看html文件是不是被完全載入進來

while (ie.Busy || ie.ReadyState != tagREADYSTATE.READYSTATE_COMPLETE)

{

Thread.Sleep(100);

}

用如上的程式碼就可以等待IE到完成。

這裡只是簡簡單單的Demo,所以用了很簡單的預計進行判斷,我們假設我們的網頁沒有Ajax,也不會出現Load的死鎖,真正的實際工作要比這個複雜一些,比如要定一個Time out,如果除了Timeout的範圍,就強行終止,以防止測試過程中的死鎖。

而如何判斷Ajax是否被載入完,不是我們這個系列的討論範圍,請關注以後的其他系列文章。

這種方法是我比較推薦的一種方法,雖然《.net軟體測試自動化之道》推薦的是第二種方法,不過我經過實際的測試,推薦第一種方法。這個方法可以比較好的處理Navigate、Submit等情況,也是WatiN使用的方法(WatiN的用法要複雜很多,考慮到了Frame等其他情況)第二種方法:通過繫結DocumentComplete,用AutoResetEvent來等待。

InternetExplorer給我們提供了DocumentComplete事件,會在IE被Load之後被呼叫,我們可以使用這個來等待。等待方法就是使用System.Threading.AutoResetEvent物件來。

所以我們需要做的是:

1. 宣告一個AutoResetEvent物件的例項,因為要在兩個方法直接呼叫,所以需要放到類的成員變數。

2. 在InternatExplorer被獲取之後,繫結DocumentComplete事件。

3. 在DocumentComplete事件中,呼叫AutoResetEvent.set()方法。

4. 在等待頁面載入的時候呼叫AutoResetEvent.WaitOne()方法

相關文章