1 前言
目前單元測試作為保證軟體品質的手段已經被越來越多的Team Dev所重視。在Web開發中,Java的單元測試手段較多,基於Junit的工具層出不窮。但是對於前台的單元測試,優秀工具少之又少。目前比較出名的JS單元測試工具有Jquery推出的Qunit,dojo推出的doh,以及比較經典的jsunit。雖然對於介面的整合測試,工具較多。但是由於介面整合測試時間成本較高,對於持續整合來說,為了測試一個小的改動就要花費大量的時間去跑介面自動化測試案例顯然不合實際。本文將對doh測試載入器作一個初步介紹,包括其robot以及其單元測試架構。
2 doh robot簡介
doh robot基於applet技術,屏蔽瀏覽器差異。通過Java代碼產生的事件來源於瀏覽器外部,不同於JS“合成”的事件。這種事件是被瀏覽器所信任的;另外doh robot可以比較真實的類比介面事件。比如按一下滑鼠,doh將先移動滑鼠到目標位置再點擊,這樣一來其附加的mouseover,mouseout事件都會被觸發。
2.1 三種doh robot
這種robot是不需要dojo支撐的,其餘的兩個robot都需要dojo功能支援
擴充的doh.robot,doh.robot只支援將滑鼠移動到某一個特定的位置,而dojo.robot支援將滑鼠移動到一個指定的html節點
擴充的dojo.robot,增強滑鼠滾輪操作。這個用的一般比較少,我也沒大看明白這個類的代碼:(
2.2 API介紹
doh.robotAPI列表如下
- sequence:function(/*Function*/ f, /*Integer, optional*/ delay, /*Integer, optional*/ duration)
- typeKeys: function(/*String||Number*/ chars, /*Integer, optional*/ delay, /*Integer, optional*/ duration)
- keyPress: function(/*Integer*/ charOrCode, /*Integer, optional*/ delay, /*Object*/ modifiers, /*Boolean*/ asynchronous)
- keyDown: function(/*Integer*/ charOrCode, /*Integer, optional*/ delay)
- keyUp: function(/*Integer*/ charOrCode, /*Integer, optional*/ delay)
- mouseClick: function(/*Object*/ buttons, /*Integer, optional*/ delay)
- mousePress: function(/*Object*/ buttons, /*Integer, optional*/ delay)
- mouseMove: function(/*Number*/ x, /*Number*/ y, /*Integer, optional*/ delay, /*Integer, optional*/ duration, /*Boolean*/ absolute)
- mouseRelease: function(/*Object*/ buttons, /*Integer, optional*/ delay)
- mouseWheel: function(/*Number*/ wheelAmt, /*Integer, optional*/ delay, /*Integer, optional*/ duration)
看到函數的名字基本上都可以知道這個函數的功能,在分別介紹函數功能前,先介紹一下共有的參數delay和duration(這兩個參數在所有的API中都是可選的):
這兩個參數都是以毫秒為單位
delay:
delay起到的作用類似於setTimeOut函數。表示經過delay所定義的時間後,函數將被執行。該參數可以不輸,預設500毫秒。
duration:
這個函數表示瀏覽器講用掉這麼長的時間來完成一個介面操作。比如doh.robot.typekeys('dij',500,1800)表示500毫秒後,robot將通過鍵盤鍵入dij三個字母,整個操作將維持1.8秒,1.8秒後執行後面操作。
對於所有的鍵盤操作,都要傳入鍵盤值。對於數字或者字母鍵可以以字串的形式傳入typeKeys函數;對於功能鍵,可以加入用dojo定義的枚舉類dojo.keys傳入鍵盤Code比如doh.robot.keyPress(dojo.keys.ENTER, 1000, {});dojo.keys的代碼位於dojo源碼包中的dojo/_base/event.js中,其對應的代碼片斷為
dojo.keys = {<br />// summary: definitions for common key values<br />BACKSPACE: 8,<br />TAB: 9,<br />CLEAR: 12,<br />ENTER: 13,<br />SHIFT: 16,<br />CTRL: 17,<br />ALT: 18,<br />PAUSE: 19,<br />CAPS_LOCK: 20,<br />ESCAPE: 27,<br />SPACE: 32,<br />PAGE_UP: 33,<br />PAGE_DOWN: 34,<br />END: 35,<br />HOME: 36,<br />LEFT_ARROW: 37,<br />UP_ARROW: 38,<br />RIGHT_ARROW: 39,<br />DOWN_ARROW: 40,<br />INSERT: 45,<br />DELETE: 46,<br />HELP: 47,<br />LEFT_WINDOW: 91,<br />RIGHT_WINDOW: 92,<br />SELECT: 93,<br />NUMPAD_0: 96,<br />NUMPAD_1: 97,<br />NUMPAD_2: 98,<br />NUMPAD_3: 99,<br />NUMPAD_4: 100,<br />NUMPAD_5: 101,<br />NUMPAD_6: 102,<br />NUMPAD_7: 103,<br />NUMPAD_8: 104,<br />NUMPAD_9: 105,<br />NUMPAD_MULTIPLY: 106,<br />NUMPAD_PLUS: 107,<br />NUMPAD_ENTER: 108,<br />NUMPAD_MINUS: 109,<br />NUMPAD_PERIOD: 110,<br />NUMPAD_DIVIDE: 111,<br />F1: 112,<br />F2: 113,<br />F3: 114,<br />F4: 115,<br />F5: 116,<br />F6: 117,<br />F7: 118,<br />F8: 119,<br />F9: 120,<br />F10: 121,<br />F11: 122,<br />F12: 123,<br />F13: 124,<br />F14: 125,<br />F15: 126,<br />NUM_LOCK: 144,<br />SCROLL_LOCK: 145<br />};
對於滑鼠事件需要傳入buttons對象,這個對象包含三個布爾型屬性left、right、middle。表示滑鼠按下的是左鍵,右鍵,滾輪,為true表示狀態是按下,沒有或者為false表示相反。舉例doh.robot.mouseClick({left:true}, 500);
對於功能性代碼的執行可以用sequence實現,可以參見第三節。
dojo.robot擴充的API為,可以支援滑鼠定位到具體的html節點
mouseMoveAt : function(/*String||DOMNode||Function*/ node, /*Integer, optional*/ delay, /*Integer, optional*/ duration, /*Number, optional*/ offsetX, /*Number, optional*/ offsetY)
2.3 功能強大的record
該功能基於dojo,在測試頁面加入dojo.require("dojox.robot.recorder"); 其功能就生效了,具體的操作步驟為
- 進入測試頁面,在測試開始點鍵入ctrl+alt+enter,這個時候回彈出alert框,告訴使用者開始錄製
- 進行介面操作,直到測試案例結束
- 再次點擊ctrl+alt+enter,出現對話方塊圖層,測試代碼自動產生,使用者copy後在其基礎上可以修改
3 舉例3.1 doh.robot測試代碼舉例
<html><br /><head><br /><mce:style><!--<br /> @import "../robot/robot.css";<br />--></mce:style><mce:style mce_bogus="1"><!--<br /> @import "../robot/robot.css";<br />--></mce:style><mce:style mce_bogus="1" mce_bogus="1"><!--<br /> @import "../robot/robot.css";<br />--></mce:style><mce:style mce_bogus="1" mce_bogus="1" mce_bogus="1"><!--<br /> @import "../robot/robot.css";<br />--></mce:style><mce:style mce_bogus="1" mce_bogus="1" mce_bogus="1" mce_bogus="1"><!--<br /> @import "../robot/robot.css";<br />--></mce:style><style mce_bogus="1" mce_bogus="1" mce_bogus="1" mce_bogus="1" mce_bogus="1"> @import "../robot/robot.css";</style><br /><mce:script src="../runner.js" mce_src="runner.js"></mce:script><br /><mce:script src="../robot.js" mce_src="robot.js"></mce:script><br /></head><br /><body><br /><form><br /><input type="text" value="hi" id="textbox" style="position:absolute; left:0px; top:20px; font-family:system;"></input><br /></form><br /><mce:script type="text/javascript"><!--<br />doh.register("doh.robot",//定義測試套<br />{<br />name:"dojorobot1",//定義測試案例<br />timeout:6900,<br />setUp:function(){<br /> document.getElementById('textbox').value="hi";<br />},<br />runTest:function(){<br /> var d=new doh.Deferred();<br /> doh.robot.mouseMove(30, 30, 500);//移動滑鼠到指定座標<br /> doh.robot.mouseClick({left:true}, 500);//點擊滑鼠<br /> doh.robot.typeKeys(" again", 500, 2500);//鍵入again<br /> doh.robot.sequence(function(){<br /> if(document.getElementById('textbox').value=="hi again"){<br /> document.getElementById('textbox').value += ": passed";//通過測試案例<br /> d.callback(true);<br /> }else{<br /> document.getElementById('textbox').value += ": failed";<br /> d.errback(new Error("Expected value 'hi again', got "+document.getElementById('textbox').value));//錯誤<br /> }<br /> }, 900);<br /> return d;<br />}<br />tearDown: function()<br />{<br />}<br />});<br />doh.run();<br />// --></mce:script><br /></body><br /></html>
doh.robot不需要dojo支援,所以可以直接通過引入doh的robot.js來實現。介面上可以看到滑鼠滑到30,30的位置,鍵入again通過測試案例
3.2 dojo.robot測試代碼舉例
dojo.require("dojo.robot");<br />dojo.addOnLoad(function(){<br />doh.register("doh.robot",<br />{<br /> name:"dojorobot1",<br /> timeout:6900,<br /> setUp:function(){<br /> document.getElementById('textbox').value="hi";<br /> },<br /> runTest:function(){<br /> var d=new doh.Deferred();<br /> doh.robot.mouseMoveAt(document.getElementById('textbox'),500);<br /> doh.robot.mouseClick({left:true}, 500);<br /> doh.robot.typeKeys(" again", 500, 2500);<br /> doh.robot.sequence(function(){<br /> if(document.getElementById('textbox').value=="hi again"){<br /> document.getElementById('textbox').value += ": passed";<br /> d.callback(true);<br /> }else{<br /> document.getElementById('textbox').value += ": failed";<br /> d.errback(new Error("Expected value 'hi again', got "+document.getElementById('textbox').value));<br /> }<br /> }, 900);<br /> return d;<br /> },<br /> tearDown:function(){}<br />});<br />doh.run();<br />});<br />
doh需要dojo支援,不過定位節點的功能增強了
4 doh測試架構頁面
doh提供了測試架構頁面,提供類似於JUNIT測試的視覺效果. 其整合過程為
- 我們和dojo的util目錄建立同級目錄demo,並在demo/doh/tests目錄下編寫測試架構頁面,代碼如下
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><br /><html><br /> <head><br /> <title>demo.doh Unit Test Runner</title><br /> <meta http-equiv="REFRESH"<br />content="0;url=../../../util/doh/runner.html?testModule=demo.doh.tests.module®isterModulePath=../demo,demo"><br /> </head><br /> <body><br /> Redirecting to D.O.H runner.<br /> </body><br /></html>
registerModulePath=../demo,demo定義了模組路徑以及模組名稱,其中模組名稱為demo,路徑為../demo,注意這裡如果寫相對路徑,是相對 於串連頁面../../../util/doh/runner.html的路徑,而不是你所寫的測試架構頁的路徑.其中testModel指定了測試模組定義檔案,這裡的檔案遵循 dojo風格,就是路徑分割符用.表示,由於模組路徑demo被定義為../demo,所以這裡的模組檔案就demo對應的路徑/doh/tests/module/js
其實這裡的demo就是我們建立的demo目錄,module.js和測試架構頁面在同一個目錄下.
dojo.provide("demo.doh.tests.module");<br />//This file loads in all the test definitions.<br />try{<br /> //Load in the widget tests.<br /> dojo.require("demo.doh.tests.widgets.DemoWidget");<br />}catch(e){<br /> doh.debug(e);<br />}
module.js定義了需要載入的測試模組demo.doh.tests.widgets.DemoWidget
- 測試模組定義JS demo.doh.tests.widgets.DemoWidget
dojo.provide("demo.doh.tests.widgets.DemoWidget");<br />if(dojo.isBrowser){<br /> //Define the HTML file/module URL to import as a 'remote' test.<br /> doh.registerUrl("demo.doh.tests.widgets.DemoWidget",<br /> dojo.moduleUrl("demo",<br />"doh/tests/widgets/DemoWidget.html"));<br />}
該函數指定了測試頁面DemoWidget.html,只要在測試頁面中寫入我前面所描述的測試代碼,就可以運行單元測試了
5 常見問題分析 5.1 運行applet出現不能載入jvm.dll錯誤:
1. 刪除所有JRE;
2. Windows系統 系統硬碟c盤:
在目錄 “C:/Documents and Settings” 下有三個檔案夾(或許你不全有,沒有關係):
“All Users.WINDOWS”
“Default User”
還有一個是你自己的使用者名稱
每個目錄下都有結構
/Application Data/
/Local Settings/Application Data
在這兩個目錄下有SUN目錄一律刪除;
3. 最後安裝你想要安裝的JRE,重新啟動電腦即可!
5.2 執行typekey函數沒有輸入
使用隨dojo1.2.1以上版本發布的doh,並且保證在你的瀏覽器視窗中IME是關閉的