簡介:IBM Rational Functional Tester 是由 IBM 推出的針對 Java、.Net 和 Web 應用程序的自動 化測試工具,擁有功能強大的編輯器並支持多種腳本語言,還集成了 ScriptAssure 技術、模式匹配功能 及數據驅動,以增強測試腳本的靈活性。借助這一工具,測試人員可以輕松地錄制或編寫腳本來進行自動 化測試,極大地提高了測試效率。
軟件在開發過程中是不斷變化的,之後各個版本間的更替也會 不可避免地引起界面、功能的變化,可以說"唯一不變的,就是變化"。這對自動化測試提出了很高的要求 :要隨需應變。自動化測試的腳本必須具有相當的自適應能力,在各種環境下都能正常工作。只有提高腳 本的復用程度和兼容性,自動化測試才有實際意義,否則隨著軟件的各個版本的變更、發布,測試人員只 能不停地去更新腳本,造成效率的低下、人力物力的極大浪費。
要達到這個目標,可以從以下兩個方面來著手:
1) 充分利用Rational Functional Tester的強大功能,比如ScriptAssure? 技術、正則表達式,數據 驅動,Rational Functional Tester API等;
2) 合理地編寫、優化腳本。提綱挈領地對測試過程進行抽象,對關鍵過程進行必要的驗證。
本文將從錄制一個簡單腳本開始,一步步對其進行改造和完善,不斷提高腳本的自適應能力,使之能 夠擺脫種種束縛,靈活可靠地在多變的測試環境中順利執行。
一 擺脫初始錄制環境的束縛
文中我們以這個簡單的Web頁面測試場景為例:用戶進入IBM網站,搜索關鍵字"lotus",驗 證"www.lotus.com"這一鏈接存在於結果集內。
首先,我們可以使用Rational Functional Tester錄制這段腳本:
1. 打開Rational Functional Tester, 新建一個"Functional Test項目",命名 為"SearchLotusProject";
2. 右鍵單擊項目管理器中的"SearchLotusProject",選擇"使用記錄器添加腳本";
3. 將新腳本命名為"SearchLotusLink",單擊"完成"按鈕;此時會彈出腳本記錄器的窗口,腳本記錄 已經開始了;
4. 依次打開IE,輸入地址:"www.ibm.com", 回車;在搜索欄裡輸入"lotus",單擊"Search"按鈕,可 以得到一個結果列表,其中就有"www.lotus.com";
5. 插入驗證點。點擊腳本記錄器上的按鈕 ,會出現"驗證點向導"窗口。按 住手形的對象選擇器,選中鏈接"www.lotus.com"( 紅框高亮顯示),以默認的設置創建一個"數據驗證點",點擊按鈕"完成"。
6. 點擊腳本記錄器上的按鈕"停止記錄",完成錄入過程。
通過以上的操作,我們得到腳本:
public void testMain(Object[] args)
{
// HTML Browser
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).click(atPoint(455,108));
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).inputKeys("www.ibm.com {ENTER}");
// Document: IBM United States: http://www.ibm.com/us/
text_q().click(atPoint(25,7));
browser_htmlBrowser(document_ibmUnitedStates(),DEFAULT_FLAGS).inputChars("lotus");
button_search().click();
httpWwwLotusCom_textVP().performTest();
}
Java語言的腳本有很好的可讀性,稍有Java知識的人便能看懂這些操作的具體含義。
接下來我們試著回放這段腳本(注意:此時搜索結果頁面尚未關閉)。點擊工具欄上的按鈕"運行 Functional Tester腳本",開始回放。
整個回放過程大約需要1分鐘,但回放日志中有2條警告信息:"對象識別較困難(在警告阈值以上)" ,並給出了識別分數和警告阈值。這是由於Rational Functional Tester所使用的ScriptAssure? 技術認 為這兩次識別是弱識別,可能存在問題。下面我們來看看具體原因:
ScriptAssure 技術給各個界面元素賦予了一定的權重,再綜合目標對象的各個屬性得出一個量化的特 征值,便於在對象之間進行比較,因此可以大大提高腳本對頻繁變更的應用程序界面的彈性適應能力, ScriptAssure技術的原理並不難懂。打開腳本資源管理器中的對象"document_htmlDocument",可以看到 這個對象在識別時,使用到3個屬性:".class",".title"和".url",權重各有不同。
權重用來標識這個屬性在識別過程中的重要程度,權重為100,表明這個屬性非常關鍵,必須和原值完 全一致;權重為0,表明這個屬性是無關緊要的。識別分數就是在這些權重的基礎上進行一定的運算而得 到的。識別對象時,如果有一個權重100的屬性值與原值不符,識別分數就要加上"權重×100",即10000 分。在這個例子中,錄制時使用的是空白的IE頁面,而回放時使用的是錄制遺留下的搜索結果頁面,導致 ".title"和".url"這兩項屬性值不符,因此它的識別分數就是".title權重×100"再加上".url權重×100" ,一共是9000分+4000分=13000分,這就是警告信息裡識別分數的由來。
至於警告信息裡提到的"警告阈值",可以在Rational Functional Tester "首選項"的ScriptAssure高 級選項裡修改。缺省值是10000,表明有一個阈值為100的重要屬性不匹配。"警告阈值"越小,則意味著識 別過程越嚴格。
對於這一例子而言,使用空白頁面和非空頁面對結果都沒有影響,只要操作對象是浏覽器即可,因此 我們可以把".title"和".url"這兩項屬性的權重設為0。修改之後,保存,再回放一次。同樣使用剛才的 結果頁面,但在回放日志裡不再有警告信息,而且回放過程也僅僅用了13秒,快速合格地完成了測試。
在錄制腳本中,除了有錄制時浏覽器頁面的限制,還有一處隱藏的限制:腳本中沒有打開浏覽器的操 作。如果沒有在回放前打開一個浏覽器頁面,回放操作肯定會失敗。幸好Rational Functional Tester的 API提供了這樣的功能,在所有Java腳本的公共父類 com.rational.test.ft.script.RationalTestScript 裡,有一個方法 startBrowser(java.lang.String url),可以用來啟動浏覽器,並打開某個URL。因此需 要在腳本的第一行前加上"startBrowser("");"用來打開一個空白頁面,然後再執行其他操作。
通過修改頁面對象的識別屬性和添加必要的Java代碼,我們完成了對腳本內容和錄制環境的解耦工作 。
二 擺脫時間的束縛
回放時,Java腳本是由Java虛擬機解釋執行,進行速度很快;而浏覽器的打開,頁面的裝入則往往由 於機器性能、網絡帶寬、服務器負載而速度緩慢。如果被測程序的運行和腳本執行的時間差過大,極有可 能導致測試的失敗,因此等待時間是我們應該妥當設置的一個要素。可以在工具級和程序級這兩個層次上 進行設置。
1) 工具級設置
在Rational Functional Tester "首選項"的"回放"選項裡,可以看到四項時間有關的回放設置。缺省 的"嘗試查找測試對象的最長時間"值為20秒。由於此處設置比較簡單,而且是一個對所有項目和腳本都有 效的全局設置,因此很難設定一個能滿足所有腳本的值。建議接受默認設置,不在這一層上做時間定制, 而是去程序級進行更為靈活的控制。
2) 程序級設置
我們可以在程序級上,對腳本做適量的加工。在某些關鍵操作後加上適當的等待,直到該操作完全執 行完畢,再繼續之後的操作。"步步為營"地執行腳本,確保每一步的前提都是正確的。
腳本可以使用以下兩種方式來添加等待時間:
定長等待
調用Java腳本的公共父類com.rational.test.ft.script.RationalTestScript裡的方法:sleep(double seconds)。這一方法可以使回放過程等待若干秒。
這種方式直觀、簡單。但缺點也是明顯的:固定的時間常常不能適應多變的真實環境:等待時間設置得 過長,無疑會拉長測試的回放時間,降低效率;等待時間設置得過短,在某些情況下,又無法起到延時應 有的效果,仍然錯過了被測對象。
不定長等待
腳本記錄器記錄下的這些頁面對象都是從接口com.rational.test.ft.object.interfaces.TestObject 繼承下來的,在TestObject中有一個方法waitForExistence()可以用以實現不定長的等待。在一定的時間 限度內,等待該對象的出現;一旦出現後就不再等待,程序繼續往下執行。最大時間限度是在"首選項"的 "回放"選項裡設置的。不定長等待既達到靈活等待的目的,又沒有浪費不必要的等待時間,是一個值得推 薦的解決方案。
本例中,我們在腳本中添加了不定長的等待設置,如下:
public void testMain(Object[] args)
{
startBrowser("");
document_htmlDocument().waitForExistence();
// HTML Browser
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).click(atPoint (455,108));
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).inputKeys("www.ibm.com {ENTER}");
// Document: IBM United States: http://www.ibm.com/us/
text_q().waitForExistence();
text_q().click(atPoint(25,7));
browser_htmlBrowser(document_ibmUnitedStates(),DEFAULT_FLAGS).inputChars ("lotus");
button_search().click();
document_ibmSearchResultsLotus().waitForExistence();
httpWwwLotusCom_textVP().performTest();
}
針對新打開的空頁面,搜索輸入框和結果頁面,分別做了相應的等待設置。正常情況下,這段腳本的 回放需要20秒鐘,耗時略多於設置等待之前,但此時的腳本在等待時間方面,處理得更為靈活。在犧牲部 分效率的同時,獲得了更高的可靠性。對於持續性的、大規模的自動化測試而言,其中的價值不言而喻。
三 擺脫空間的束縛
腳本記錄器將記錄下來的對象保存在該腳本的 "專用測試對象圖"中,而且是以樹形結構保存。專用測 試對象圖不僅保存了對象本身的相關屬性,連它和其他對象的相對關系也一並保存下來。
這段腳本使用方法document_htmlDocument()來調用頁面的Document對象,使用方法text_q()來調用搜 索輸入框,使用方法 button_search()來調用搜索按鈕。這些方法是由腳本SearchLotusLink的父類 SearchLotusLinkHelper定義的:
protected GuiTestObject document_htmlDocument()
{
return new GuiTestObject(getMappedTestObject("document_htmlDocument"));
}
protected TextGuiTestObject text_q()
{
return new TextGuiTestObject(getMappedTestObject("text_q"));
}
protected GuiTestObject button_search()
{
return new GuiTestObject(getMappedTestObject("button_search"));
}
腳本回放時,Rational Functional Tester利用"專用測試對象圖"進行靜態識別,可以從浏覽器這個 頂級容器開始,層層深入地定位到指定的某個對象。但這種呆板的對象查找方式也留下了很大的隱患: Web頁面裡層次結構的變化屢見不鮮,而這種變化對於對象的查找而言,有著致命的影響。在這種模式下 要想適應頁面層次結構的變化,只能重新錄制對象,生成新的"專用測試對象圖"。代價如此之大的維護方 式使得自動化測試幾乎沒有可行性。
因此必須要將對象的識別同具體的"專用測試對象圖"分開,實現對象的動態識別。頁面對象都有一個 共同的父類:com.rational.test.ft.object.interfaces.TestObject,而它的方法find(Subitem properties)正是用來在某個特定范圍內查找滿足條件的所有對象。借助它,我們可以對 SearchLotusLinkHelper 進行改造,使其與"專用測試對象圖"不再緊密地耦合在一起。
改造後,回放過程中所需要用到的頁面對象都是在當前浏覽器中即時查找得到的。通過目標對象的類 型和某個屬性值來定位目標對象,脫離了"專用測試對象圖"中樹形結構的約束。SearchLotusLinkHelper 的部分內容如下:
protected GuiTestObject document_htmlDocument() {
return new GuiTestObject(findTestObjectInBrowser (".class","Html.HtmlDocument",null,null));
}
protected TextGuiTestObject text_q() {
return new TextGuiTestObject(findTestObjectInBrowser (".class","Html.INPUT.text",".id","q"));
}
protected GuiTestObject button_search() {
return new GuiTestObject(findTestObjectInBrowser (".class","Html.INPUT.image",".value","Search"));
}
protected GuiTestObject link_httpWwwLotusCom() {
return new
GuiTestObject(findTestObjectInBrowser (".class","Html.A",".text","http://www.lotus.com/"));
}
protected TestObject findTestObjectInBrowser(String property1, String value1, String property2,
String value2)
{
TestObject[] foundTOs ;
//在當前浏覽器頁面中查找
if(null==property2)
foundTOs = browser_htmlBrowser().find(atDescendant(property1,value1)) ;
else
foundTOs = browser_htmlBrowser().find(atDescendant (property1,value1,property2,value2)) ;
//如果沒有找到滿足條件的TestObject
if(foundTOs.length<1)
{
throw new com.rational.test.ft.ObjectNotFoundException("Can NOT find TestObject with
"+property1+"<"+value1+">,"+property2+"<"+value2+">");
}
//如果找到多個TestObject滿足條件,
else if(foundTOs.length>1)
{
throw new AmbiguousRecognitionException("Found multi-TestObject with
"+property1+"<"+value1+">,"+property2+"<"+value2+">");
}
//返回唯一的查找結果
return foundTOs[0];
}
與此同時,針對腳本SearchLotusLink裡為選擇操作對象而記錄下的的鼠標操作,及驗證點。我們也同 樣對其進行抽象,攫取其中的關鍵行為,代之以程序動作。比如:
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).click(atPoint (455,108));
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).inputKeys("www.ibm.com {ENTER}");
這兩行腳本的操作是在浏覽器的地址欄內輸入URL,再按下回車。這個動作如果借助Rational Functional Tester所提供的API來表示,可以簡化很多:
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).loadUrl("www.ibm.com");
再以程序的方式生成驗證點並驗證,不與腳本相關的數據結構發生干系。經過優化和精煉後的腳本 SearchLotusLink如下:即使將來頁面布局和結構發生變化,這段腳本仍然能正確地定位到搜索輸入框和 搜索按鈕。
public void testMain(Object[] args)
{
startBrowser("");
document_htmlDocument().waitForExistence();
// HTML Browser
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).loadUrl ("www.ibm.com");
// Document: IBM United States: http://www.ibm.com/us/
text_q().waitForExistence();
text_q().setText("lotus");
button_search().click();
link_httpWwwLotusCom().waitForExistence();
IFtVerificationPoint vp1 =
vpManual("vp1","http://www.lotus.com/",link_httpWwwLotusCom().getProperty (".href"));
vp1.performTest();
}
四 擺脫語言的束縛
如今的軟件大多都要求支持"全球化",能夠適應多種語言環境。就用戶界面而言,需要在不同的語言 環境中展示出本地語言界面。多語言環境下的人工測試往往不盡如人意,自動化測試反而更為迅速和准確 。
我們仍然以這個場景為例,所不同的是:用戶進入IBM中國網站。再搜索關鍵字"lotus",驗 證"www.lotus.com"這一鏈接存在於結果集內。測試過程的邏輯並沒有變化,但界面截然不同, "Search" 按鈕在這裡顯示為"搜索"按鈕。
為了拓展腳本SearchLotusLink對多語言的支持能力,我們在SearchLotusLinkHelper查找對象的過程 中,加上一個中間層,通過它來銜接固定的操作邏輯和多變的界面。原先的方法 findTestObjectInBrowser(String property1, String value1, String property2, String value2)只 是按這兩個屬性值找出合適的對象;現在則要對屬性做適當的轉換,使它能適應其他語言環境,在不同語 言的界面裡都能定位到這個頁面對象。
將Rational Functional Tester切換到"Java透視圖",在SearchLotusLinkHelper所在的resources包 中,添加一個類:utilities,來實現對多語言環境的支持。這裡為簡單起見,我們將URL和搜索按鈕的文 本的多語言表示直接保存在這個類裡。用戶可以通過它來獲取某一屬性在特定語言環境下的表示。
public class utilities {
/**
* Script Name : <b>utilities</b>
* Generated : <b>2005-10-25 16:51:51</b>
* Description : Functional Test Script
* Original Host : WinNT Version 5.1 Build 2600 (S)
*
* @since 2005/10/25
* @author zhangguojun
*/
public static String EN_LOCALE = "en";
public static String CN_LOCALE = "cn";
public String CurrentLocale = EN_LOCALE;
private Hashtable textRepositoryForEN = new Hashtable();
private Hashtable textRepositoryForCN = new Hashtable();
private static utilities _instance = null;
private utilities(){
textRepositoryForEN.put("Search","Search");
textRepositoryForEN.put("IBMurl","www.ibm.com");
textRepositoryForCN.put("Search","搜索");
textRepositoryForCN.put("IBMurl","www.ibm.com/cn");
}
public static utilities getInstance() {
if (null == _instance)
_instance = new utilities();
return _instance;
}
public void setCurrentLocale(String locale)
{
CurrentLocale=locale;
}
public String getLocalText(String textId)
{
return getLocalText(CurrentLocale,textId);
}
public String getLocalText(String locale, String textId)
{
String returnVal = null;
if(locale.equals(EN_LOCALE))
returnVal = (String)textRepositoryForEN.get(textId);
else if(locale.equals(CN_LOCALE))
returnVal = (String)textRepositoryForCN.get(textId);
if(null==returnVal)
return textId;
else
return returnVal;
}
}
進而我們對SearchLotusLinkHelper中方法findTestObjectInBrowser(String property1, String value1, String property2, String value2)的做相應的修改,使它能夠得到當前語言環境下屬性的表述 形式。
protected TestObject findTestObjectInBrowser(String property1, String value1,
String property2, String value2)
{
TestObject[] foundTOs ;
String val1 = utilities.getInstance().getLocalText(value1);
String val2;
//在當前浏覽器頁面中查找
if(null==property2)
foundTOs = browser_htmlBrowser().find(atDescendant(property1,val1)) ;
else
{
val2 = utilities.getInstance().getLocalText(value2);
foundTOs = browser_htmlBrowser().find(atDescendant (property1,value1,property2,val2)) ;
}
//如果沒有找到滿足條件的TestObject
if(foundTOs.length<1)
{
throw new com.rational.test.ft.ObjectNotFoundException("Can NOT find TestObject with
"+property1+"<"+value1+">,"+property2+"<"+value2+">");
}
//如果找到多個TestObject滿足條件,
else if(foundTOs.length>1)
{
throw new AmbiguousRecognitionException("Found multi-TestObject with
"+property1+"<"+value1+">,"+property2+"<"+value2+">");
}
//返回唯一的查找結果
return foundTOs[0];
}
最後,我們在腳本SearchLotusLink中加上一個開關,用來設置當前所使用的語言環境。我們只需要簡 單地設置一下這個開關,同樣的腳本就可以在中英文兩種環境下進行測試。按這種模式,測試還可以延伸 到其它語言環境中。
public void testMain(Object[] args)
{
//設置當前所使用的語言環境
utilities.getInstance().setCurrentLocale(utilities.CN_LOCALE);
startBrowser("");
document_htmlDocument().waitForExistence();
// HTML Browser
browser_htmlBrowser(document_htmlDocument(),DEFAULT_FLAGS).loadUrl (utilities.getInstance().
getLocalText("IBMurl"));
// Document: IBM United States: http://www.ibm.com/us/
text_q().waitForExistence();
text_q().setText("lotus");
button_search().click();
link_httpWwwLotusCom().waitForExistence();
IFtVerificationPoint vp1 =
vpManual("vp1","http://www.lotus.com/",link_httpWwwLotusCom().getProperty (".href"));
vp1.performTest();
}
六 總結和展望
本文通過一個針對Web頁面的測試用例,由淺入深地闡明了如何利用Rational Functional Tester創建 隨需應變的自動化測試腳本。腳本在經過優化和改造後,對多變的回放環境、等待時間、頁面變化及多語 言環境都有了較強的自適應能力。使腳本在不經維護或很少維護的情況下,也能在各種環境下順利地執行 測試。
文中只是介紹了一個基本思路和簡單實現。在自動化測試的大規模應用中,需要對utilities類、 findTestObjectInBrowser()方法等輔助功能做進一步的完善和擴充,衍生為一個支持框架,對腳本的記 錄、回放、靈活性、健壯性加以全方位的支持。"軟硬結合",在這個靈活的軟框架的輔佐下,更好地發揮 出Rational Functional Tester工具的強大功能。