在計算機編程中,單元測試(又稱爲模塊測試)是針對程序模塊(軟件設計的最小單位)來進行正確性檢驗的測試工做。程序單元是應用的最小可測試部件。在過程化編程中,一個單元就是單個程序、函數、過程等;對於面向對象編程,最小單元就是方法,包括基類(超類)、抽象類、或者派生類(子類)中的方法。javascript
每一個理想的測試案例獨立於其它案例;爲測試時隔離模塊,常用stubs、mock 或fake等測試馬甲程序。單元測試一般由軟件開發人員編寫,用於確保他們所寫的代碼符合軟件需求和遵循開發目標。css
單元測試的目標是隔離程序模塊並證實這些單個模塊是正確的。單元測試能確保在開發過程的早期就能發現問題,是爲了讓程序「死得更早」。咱們應該從開發的早期就爲全部函數和方法編寫單元測試,可讀性強的單元測試可使程序員方便地檢查代碼片段是否依然正常工做。良好設計的單元測試案例覆蓋程序單元分支和循環條件的全部路徑。採用這種自底向上的測試路徑,先測試程序模塊再測試模塊的集合,一旦變動致使錯誤發生,藉助於單元測試能夠快速定位並修復錯誤。html
單元測試在後臺開發中很是流行和普及,好比JAVA開發者的JUnit等,而在前端開發中則使用的很是少。究其緣由,主要是單元測試更適用於邏輯代碼的測試,這對於JAVA等後臺編程語言來講測試起來很是方便,可是前端開發不少時候要要UI打交道,UI相關的代碼不是不能夠進行單元測試,但的確很麻煩,比起邏輯代碼來講困難多了,這就致使了單元測試在前端開發沒有普及起來。前端
可是隨着單元測試的普及,尤爲是敏捷開發的推進,涌現了許多優秀的JavaScript單元測試框架,如QUnit、Jasmine等。全部的這些框架基本上都能對Javascript代碼進行很好的測試,固然UI部分的代碼測試同樣比較麻煩,可是咱們能夠經過精心構造咱們的測試代碼來測試部分UI代碼。可是每一個框架都不是萬能的,它們都有各自擅長的領域,下面選取了幾個具備表明性的框架進行介紹。java
QUnit是jQuery團隊開發的JavaScript單元測試工具,功能強大且使用簡單。目前全部的JQuery代碼都使用QUnit進行測試,原生的JavaScript也可使用QUnit。node
最初,John Resig將QUnit設計爲jQuery的一部分。2008年,QUnit纔有了本身的名字、主頁和API文檔,也開始容許其餘人用它來作單元測試。但當時QUnit仍是基於jQuery的。直到2009年,QUnit才能夠徹底的獨立運行。git
使用起來很是方便,有漂亮的外觀和完整的測試功能(包括異步測試);程序員
很是簡單,容易上手,目前公開的API只有19個;github
不須要依賴其它任何軟件包或框架,只要能運行JS的地方就能夠,QUnit自己只有一個JS文件和CSS文件,固然若是須要能夠和jQuery等其它框架集成;web
不只支持在瀏覽器中測試,還支持在Rhino和node.js等後端測試。
對自動化支持很差,很難和Ant、Maven或自動構建等工具集成,主要用在瀏覽器中進行測試。
QUnit全部的API能夠分爲三類:Setup,Assertions,Asynchronous Testing,下面就分別對這些API作些介紹:
Setup:
test( name, [expected], testFun ) 表明QUnit中的一個測試
name:要測試的名稱,好比「加法函數」或「add」等
expected:可選參數,用來表示該測試函數的斷言的數量,是個正整數
testFun:一個函數,全部的測試代碼都應該包括在該函數裏,一般這是一個匿名函數。
例:
test(「add function」, 1, function() { equal(add(1, 2), 3); });
asyncTest( name, [expected], testFun ) 表明QUnit中的一個異步測試,參數同test
expect( amount ) 用在測試函數中,用於聲明測試斷言的數量,這個函數和test中的expected參數的做用是同樣的。主要做用就是檢查你聲明的個數和你寫的斷言的實際個數是否一致。
module( name, [lifecycle] ) 主要用於測試函數的分組,一個module函數爲一個分組,好比module(「validate」)表示後面的測試用例都是validate相關的代碼,或者module(「common.js」),代表後面的測試用例都是common.js裏面的代碼。一個測試文件能夠寫多個module。
name:分組或者模塊的名稱
lifecycle:可選參數,它是一個對象,能夠設置setup和teardown回調函數
例:
module(「common.js」, { setup:function(){}, teardown: function() {} } );
setup:在module開始以前執行,能夠爲該module下面的測試代碼作一些準備工做
teardown:將會在該module的全部測試代碼執行後執行,好比作一些清理還原工做等。
QUnit.init( ) 用於初始化QUnit測試框架,一般這個函數是不須要咱們手工調用的。
QUnit.reset( ) 重設函數,一般是在每一個test函數執行後由QUnit本身調用來重設整個QUnit測試環境,固然必要時咱們本身也能夠調用它來複原,不經常使用。
Assertions:
ok( state, [message] ) 斷言。state值爲true時表示經過,不然失敗。
equal( actual, expected, [message] ) 比較參數actual和expected是否相等,至關於 ==
notEqual( actual, expected, [message] ) 比較兩個參數是否不相等,至關於 !=
deepEqual( actual, expected, [message] ) 主要用於數組和對象等類型的值是否相等,會遞歸遍歷它們所包含的值是否相等。
notDeepEqual( actual, expected, [message] ) 主要用於數組和對象等類型的值是否不相等,會遞歸遍歷它們所包含的值是否不相等。
strictEqual( actual, expected, [message] ) 比較兩個參數是否嚴格相等,至關於 ===
notStrictEqual( actual, expected, [message] ) 比較兩個參數是否不嚴格相等,至關於 !==
throws( block, expected, [message] ) 測試block函數是否拋出一個異常,拋出則經過,不拋則失敗。
block:咱們要測試的函數
expected:可選參數,是一個類型,用來驗證第一個函數拋出的異常是不是咱們預期的類型。
例:
function CustomError( message ) { this.message = message; } CustomError.prototype.toString = function() { return this.message; }; throws( function() { throw new CustomError(「some error description」); }, CustomError, "raised error is an instance of CustomError" );
Asynchronous Testing:
stop( [increment] ) 中止測試的運行,用於異步測試。在異步測試時通常先把QUnit的test runner停下來。
increment:增長中止的時間。
start( [decrement] ) 當異步調用成功後就應該把中止的test runner啓動起來讓它接着往前跑
decrement:用來減小中止的時間。
例:
test( "a test", function() { stop(); var result = null; $.ajax( url, {}, function(data){ result = data; } ); setTimeout(function() { equals(result, "success" ); start(); }, 150 ); });
test.html
導入qunit.css,qunit.js
依次導入被測試文件src.js和測試文件test.js
src.js裏是咱們要測試的一些函數
test.js裏放咱們的測試
打開test.html,顯示:
若是指望值與函數執行的結果不一致,會報錯:
test.js
test.html顯示:
指望值與結果不符,測試不經過。
與瀏覽器自動化測試工具集成的接口:
都是QUnit自動調用的一些函數,通常不用改,也能夠本身定製
QUnit.log(Function({ result, actual, expected, message })) 這個接口會在每一個斷言執行後被自動調用
result:斷言是否經過
message:斷言裏的message參數
例:
QUnit.log(function(details){ alert(「Log: 」 + details.result + 「 」 + details.message); })
QUnit.testStart(Function({ name })) 在每一個測試函數執行前被自動調用
name:測試函數中的name參數值
QUnit.testDone(Function({ name, failed, passed, total })) 在每一個測試函數結束後執行被自動調用
name:同上
failed:指失敗斷言的個數
passed:指成功斷言的個數
total:全部斷言的個數
QUnit.moduleStart(Function({ name })) 在每一個module全部的測試代碼執行前被自動調用
name:module函數中name參數的值
QUnit.moduleDone(Function({ name, failed, passed, total })) 在每一個module全部的測試代碼執行完以後被自動執行
failed:指失敗斷言的個數
passed:指成功斷言的個數
total:指全部斷言的個數
QUnit.begin(Function()) 在全部的測試代碼調用以前運行
QUnit.done(Function({ failed, passed, total, runtime })) 在全部的測試代碼調用以後運行
failed:指失敗斷言的個數
passed:指成功斷言的個數
total:指全部斷言的個數
runtime:全部代碼的執行時間
API及下載:http://api.qunitjs.com/
參考:http://www.weakweb.com/articles/255.html
http://www.iteye.com/topic/981253
Jasmine是一個有名的JavaScript單元測試框架,它是獨立的行爲驅動開發框架,語法清晰易懂。
行爲驅動開發(BDD):是一種敏捷軟件開發的技術,它鼓勵軟件項目中的開發者、QA和非技術人員或商業參與者之間的協做。BDD最初是由Dan North在2003年命名,它包括驗收和客戶測試驅動等的極限編程的實踐,做爲對測試驅動開發的迴應。在過去的數年裏,獲得了極大的發展。
BDD的重點是經過與利益相關者的討論取得對預期的軟件行爲的清醒認識。它經過用天然語言書寫非程序員可讀的測試用例擴展了測試驅動開發方法。行爲驅動開發人員使用混合了領域中統一的語言的母語語言來描述他們的代碼的目的。這讓開發者得以把精力集中在代碼應該怎麼寫,而不是技術細節上,並且也最大程度的減小了將代碼編寫者的技術語言與商業客戶、用戶、利益相關者、項目管理者等的領域語言之間來回翻譯的代價。
BDD的作法包括:
l 確立不一樣利益相關者要實現的遠景目標
l 使用特性注入方法繪製出達到這些目標所須要的特性
l 經過由外及內的軟件開發方法,把涉及到的利益相關者融入到實現的過程當中
l 使用例子來描述應用程序的行爲或代碼的每一個單元
l 經過自動運行這些例子,提供快速反饋,進行迴歸測試
l 使用「應當(should)」來描述軟件的行爲,以幫助闡明代碼的職責,以及回答對該軟件的功能性的質疑
l 使用「確保(ensure)」來描述軟件的職責,以把代碼自己的效用與其餘單元(element)代碼帶來的邊際效用中區分出來。
l 使用mock做爲還未編寫的相關代碼模塊的替身
BDD特性注入:一個公司可能有多個會帶來商業利益的不一樣願景,一般包括盈利、省錢或保護錢。一旦某個願景被開發小組肯定爲當前條件下的最佳願景,他們將須要更多的幫助來成功實現這個遠景。
而後肯定該願景的主要利益相關者,會帶入其餘的利益相關者。每一個相關者要定義爲了實現該願景他們須要完成的目標。例如,法務部門可能要求某些監管要獲得知足。市場營銷負責人可能要參加將使用該軟件的用戶的社區。安全專家須要確保該軟件不會受到SQL注入的攻擊。
經過這些目標,會定義出要實現這些目標所須要的大概的題目或者特性集合。例如,「容許用戶排序貢獻值」或「交易審計」。從這些主題,能夠肯定用戶功能以及用戶界面的第一批細節。
它是基於行爲驅動開發實現的測試框架,它的語法很是貼近天然語言,簡單明瞭,容易理解。
能很方便的和Ant、Maven等進行集成進行自動化測試,也能夠方便和Jekins等持續集成工具進行集成,能夠生成測試結果的XMl文檔。
它有豐富的API,同時用戶也支持用戶擴展它的API,這一點不多有其它框架可以作到。
使用方便簡單,只須要引入兩個js文件便可
不只支持在瀏覽器中測試,還支持在Rhino和node.js等後端測試。
對於Ruby語言有特別的支持,可以很是方便的集成到Ruby項目中去
在瀏覽器中的測試界面不如QUnit美觀、詳細。
it(string, function) 一個測試Spec
string:測試名稱
function:測試函數
describe (string, function) 一個測試組開始於全局函數describe,一個describe是一個it的集合。describe包含n個it,一個it包含n個判斷斷言 Suite
string:測試組名稱
function:測試組函數
describe("測試add()函數", function() { it("1 + 1 = 2", function(){ expect(add(1, 1)).toBe(2); }); });
beforeEach(function) 定義在一個describe的全部it執行前作的操做
afterEach(function) 定義在一個describe的全部it執行後作的操做
expect(a).matchFunction(b)
expect(a).not.matchFunction(b) 指望a和b知足匹配方式matchFunction
matchFunctions:
toBe 至關於===,處理簡單字面值和變量
it("toBe至關於===", function(){ var a = 12; var b = a; expect(a).toBe(b); expect(a).not.toBe(null); expect(false == 0).toBe(true); }); it("toBe不能當==用", function(){ expect(false).toBe(0); });
toEqual 處理簡單字面值和變量,並且能夠處理對象,數組
it("toEqual能夠處理字面值,變量和對象", function(){ var a = 12; expect(a).toEqual(12); var foo = {key : "key"}; var bar = {key : "key"}; expect(foo).toEqual(bar); var arr1 = []; arr1["p1"] = "string1"; var arr2 = []; arr2["p1"] = "string1"; var obj = {}; obj["p1"] = "string1"; expect(arr1).toEqual(arr2); expect(arr1).toEqual(obj); });
toMatch 按正則式檢索。
it("toMatch匹配正則式", function(){ var message = "foo bar baz"; expect(message).toMatch(/bar/); expect(message).toMatch("bar"); expect(message).not.toMatch(/quux/); expect(message).toMatch(/^f/); expect(message).not.toMatch(/f$/); });
toBeDefined 是否已聲明且賦值
it("toBeDefined檢測變量非undefined", function(){ var a = { key : "key"}; expect(a.key).toBeDefined(); expect(a.foo).not.toBeDefined(); //expect(c).not.toBeDefined(); //未聲明出錯 var b; expect(b).not.toBeDefined(); });
對象.未聲明屬性.not.toBeDefined(); 經過
未聲明變量.not.toBeDefined(); 報錯
toBeUndefined 是否undefined
toBeNull 是否null
toBeTruthy 若是轉換爲布爾值,是否爲true
toBeFalsy 若是轉換爲布爾值,是否爲false
toContain 數組中是否包含元素(值)。只能用於數組,不能用於對象
it("toContain檢驗數組中是否包含元素(值)", function(){ var a = ["foo", "bar", "baz"]; expect(a).toContain("bar"); });
toBeLessThan 數值比較,小於
toBeGreaterThan 數值比較,大於
toBeCloseTo 數值比較時定義精度,先四捨五入後再比較
it("toBeCloseTo數值比較,指定精度,先四捨五入再比較", function() { var pi = 3.1415926, e = 2.78; expect(pi).toBeCloseTo(e, 0); expect(pi).not.toBeCloseTo(e, 0.1); });
toThrow 檢驗一個函數是否會拋出一個錯誤
it("toThrow檢驗一個函數是否會拋出一個錯誤", function() { var foo = function() { return 1 + 2; }; var bar = function() { return a + 1; }; expect(foo).not.toThrow(); expect(bar).toThrow(); });
注:describe可嵌套
xdescribe 和 xit:路過不執行,結果不顯示。像display:none。點控制欄中skipped顯示
Spy 存儲函數的被調用狀況和參數(函數監視器,記錄被調用狀況,但函數並不真執行)
describe("對spy函數的測試", function() { var foo, bar = null; beforeEach(function() { foo = { setBar: function(value) { bar = value; } }; spyOn(foo, 'setBar'); //foo爲spy函數 foo.setBar(123); foo.setBar(456, 'another param'); }); it("測試foo函數是否被調用過", function() { expect(foo.setBar).toHaveBeenCalled(); }); it("測試foo函數被調用的次數", function() { expect(foo.setBar.calls.length).toEqual(2); }); it("測試foo函數被調用時傳入的參數", function() { expect(foo.setBar).toHaveBeenCalledWith(123); expect(foo.setBar).toHaveBeenCalledWith(456, 'another param'); }); it("上一次被調用的參數", function() { expect(foo.setBar.mostRecentCall.args[0]).toEqual(456); }); it("全部被調用的狀況存在一個數組裏", function() { expect(foo.setBar.calls[0].args[0]).toEqual(123); }); it("函數並未真的執行", function() { expect(bar).toBeNull(); }); });
Spy addCallThrough 函數監視器,但函數真的執行
describe("對spy函數的測試,函數真的執行", function() { var foo, bar, fetchedBar; beforeEach(function() { foo = { setBar: function(value) { bar = value; }, getBar: function() { return bar; } }; //spyOn(foo, "setBar"); //若是加上這句,setBar不真的執行,後兩個spec不經過 spyOn(foo, 'getBar').andCallThrough(); foo.setBar(123); fetchedBar = foo.getBar(); }); it("測試foo中getBar函數是否被調用過", function() { expect(foo.getBar).toHaveBeenCalled(); }); it("foo中setBar函數真的執行了", function() { expect(bar).toEqual(123); }); it("foo中getBar函數真的執行了", function() { expect(fetchedBar).toEqual(123); }); });
Spy andReturn 函數監視器,函數不真的執行。指定監視的函數的返回值
describe("A spy, when faking a return value", function() { var foo, bar, fetchedBar; beforeEach(function() { foo = { setBar: function(value) { bar = value; }, getBar: function() { return bar; } }; spyOn(foo, 'getBar').andReturn(745); //指定getBar函數返回745 foo.setBar(123); fetchedBar = foo.getBar(); }); it("測試foo中getBar函數是否被調用過", function() { expect(foo.getBar).toHaveBeenCalled(); }); it("不影響未被監視的其它函數", function() { expect(bar).toEqual(123); }); it("指定的返回值745", function() { expect(fetchedBar).toEqual(745); }); });
Spy addCallFake 替代被監視的函數,原函數不執行
describe("替代被監視的函數,原函數不執行", function() { var foo, bar, fetchedBar; beforeEach(function() { foo = { setBar: function(value) { bar = value; }, getBar: function() { alert("frostbelt"); return bar; } }; spyOn(foo, 'getBar').andCallFake(function() { return 1001; }); foo.setBar(123); fetchedBar = foo.getBar(); }); it("測試foo中getBar函數是否被調用過", function() { expect(foo.getBar).toHaveBeenCalled(); }); it("不影響未被監視的其它函數", function() { expect(bar).toEqual(123); }); it("getBar被addCallFake指定的匿名函數代替,getBar不執行", function() { expect(fetchedBar).toEqual(1001); }); });
若是你沒有什麼可監視的又實在想監視一下,該咋辦?本身create一個被監視函數。。
jasmine.createSpy(functionId)
describe("本身造一個被監視函數。啊,我凌亂了。。", function() { var whatAmI; beforeEach(function() { whatAmI = jasmine.createSpy('whatAmI'); whatAmI("I", "am", "a", "spy"); }); it("有個id,是createSpy的傳入函數,用於報錯", function() { expect(whatAmI.identity).toEqual('whatAmI') }); it("是否被調用", function() { expect(whatAmI).toHaveBeenCalled(); }); it("被調用的次數", function() { expect(whatAmI.calls.length).toEqual(1); }); it("被調用的參數", function() { expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy"); }); it("最近一次被調用", function() { expect(whatAmI.mostRecentCall.args[0]).toEqual("I"); }); });
有時須要監視一個對象的不少方法,用createSpyObj添加方法數組
jasmine.createSpyObj(obj, methodArray)
describe("有時須要監視一個對象的不少個方法,用createSpyObj添加數組", function() { var tape; beforeEach(function() { tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']); tape.play(); tape.pause(); tape.rewind(0); }); it("tape對象的這四個方法已被定義", function() { expect(tape.play).toBeDefined(); expect(tape.pause).toBeDefined(); expect(tape.stop).toBeDefined(); expect(tape.rewind).toBeDefined(); }); it("四個方法是否被調用", function() { expect(tape.play).toHaveBeenCalled(); expect(tape.pause).toHaveBeenCalled(); expect(tape.rewind).toHaveBeenCalled(); expect(tape.stop).not.toHaveBeenCalled(); }); it("被調用時傳入的參數", function() { expect(tape.rewind).toHaveBeenCalledWith(0); }); });
jasmine.any 類型判斷。instanceof
describe("類型匹配", function() { it("至關於instanceof", function() { expect({}).toEqual(jasmine.any(Object)); expect(12).toEqual(jasmine.any(Number)); }); it("也能夠用於spy", function() { var foo = jasmine.createSpy('foo'); foo(12, function() { return true }); expect(foo).toHaveBeenCalledWith(jasmine.any(Number), jasmine.any(Function)); //foo被調用時的參數 類型判斷 }); });
jasmine.Clock.useMock() jasmine本身控制時間,實現異步調試,減小等待
jasmine.Clock.tick(n:uint) 向前n毫秒
describe("jasmine本身控制時間,實現異步調試,減小等待", function() { var timerCallback; beforeEach(function() { timerCallback = jasmine.createSpy('timerCallback'); jasmine.Clock.useMock(); }); it("setTimeout", function() { setTimeout(function() { timerCallback(); }, 100); expect(timerCallback).not.toHaveBeenCalled(); jasmine.Clock.tick(101); expect(timerCallback).toHaveBeenCalled(); }); it("setInterval", function() { setInterval(function() { timerCallback(); }, 100); expect(timerCallback).not.toHaveBeenCalled(); jasmine.Clock.tick(101); expect(timerCallback.callCount).toEqual(1); jasmine.Clock.tick(50); expect(timerCallback.callCount).toEqual(1); jasmine.Clock.tick(50); expect(timerCallback.callCount).toEqual(2); }); });
注:在這種環境下setTimeout和setInterval的callback爲同步的,系統時間再也不影響執行
runs(function) waitsFor(function, message, millisec) Jasmine異步調試 按本身的理解寫個例子
describe("jasmine異步調試,對ajax結果的斷言", function(){ var data, flag = false; it("ajax是否按時返回了正確結果", function(){ runs(function(){ $.post( url, {}, function(data){ flag = true; data = data.someAttr; } ); }); waitsFor(function(){ //flag爲true或到2秒時執行 2秒內返回true則執行最後一個runs,到時未返回則本spec出錯,返回第二個參數錯誤信息 return flag; }, "ajax在指定時間2秒內未返回", 2000); runs(function(){ //直到waitsFor返回true時執行 expect(data).toEqual("someThing"); }) }); });
注:it是一個spec,包含
runs(function)
waitsFor(function, message, millsec)
runs(function)
第一個runs裏有一些異步的代碼
waitsFor中的funciton若是在millsec內返回true,執行最後一個runs
若是在millsec內不能返回true,spec不經過,顯示錯誤信息message
原文代碼:
在測試的頁面里加入如下代碼:
<script type="text/javascript"> (function() { var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; var trivialReporter = new jasmine.TrivialReporter(); jasmineEnv.addReporter(trivialReporter); jasmineEnv.specFilter = function(spec) { return trivialReporter.specFilter(spec); }; var currentWindowOnload = window.onload; window.onload = function() { if (currentWindowOnload) { currentWindowOnload(); } execJasmine(); }; function execJasmine() { jasmineEnv.execute(); } })(); </script>
導入jasmine.css jasmine.js jasmine-html.js
src.js(源代碼) test.js(存放describes)
參考:http://www.weakweb.com/articles/255.html
http://pivotal.github.com/jasmine/
下載:https://github.com/pivotal/jasmine/downloads
a) 簡介
JsTestDriver是一個JavaScript單元測試工具,易於與持續構建系統相集成並可以在多個瀏覽器上運行測試輕鬆實現TDD風格的開發。當在項目中配置好JsTestDriver之後,如同junit測試java文件通常,JsTestDriver能夠直接經過運行js文件來進行單元測試。JsTestDriver框架自己就是JAVA的jar包,須要在本地運行並監聽一個端口。
b) 優勢
能夠一次測試多個瀏覽器,使用方法是在啓動服務時能夠將多個瀏覽器的路徑做爲參數傳進去。能夠在多臺機器上的瀏覽器中運行,包括移動設備。
測試運行得很快,由於不須要將結果添加到DOM中呈現出來,它們可以同時在任意多的瀏覽器中運行,未修改的文件瀏覽器會從緩存提取。
不須要HTML配件文件,僅僅只需提供一個或多個腳本和測試腳本,測試運行器運行時會建立一個空文件。
能很方便的和Ant、Maven等進行集成進行自動化測試,也能夠方便和Jekins等持續集成工具進行集成,能夠生成測試結果的XML文檔。
有Eclipse和IntelliJ插件,能夠很方便的在這兩個IDE中進行測試,和JUnit很像。
支持其它測試框架,能夠測試其它測試框架寫的測試代碼,好比有對應的插件能夠將QUnit和Jasmine測試代碼轉換成JsTestDriver的測試代碼。
c) 不足
不能在瀏覽器中測試,只能經過自動化工具或控制檯運行。生成的結果不夠直觀。
安裝使用稍微有點麻煩,依賴於JAVA環境。
d) API
assert(msg, value)
assertTrue(msg, value)
assertFalse(msg, value)
assertEquals(msg, expected, actual)
assertNotEquals(msg, expected, actual)
assertSame(msg, expected, actual)
assertNotSame(msg, expected, actual)
assertNull(msg, value)
assertNotNull(msg, value)
assertUndefined(msg, value)
assertNotUndefined(msg, value)
assertNaN(msg, number)
assertNotNaN(msg, number)
assertException(msg, callback, type)
assertNoException(msg, callback)
assertArray(msg, arrayLike)
assertTypeOf(msg, type, object)
assertBoolean(msg, value)
assertFunction(msg, value)
assertNumber(msg, value)
assertObject(msg, value)
assertString(msg, value)
assertMatch(msg, pattern, string)
assertNoMatch(msg, pattern, string)
assertTagName(msg, tagName, element)
assertClassName(msg, className, element)
assertElementId(msg, id, element)
assertInstanceOf(msg, constructor, object)
assertNotInstanceOf(msg, constructor, object)
API按字面意思理解便可,不一一標註
e) 使用
前提:
安裝java環境
下載JsTestDriver.jar
目錄:
JsTestDriver.jar
jsTestDriver.conf //配置文件,默認名稱,若是用其它名稱,須要指定config參數
src
----src.js
test
----test.js
jsTestDriver.conf:
src.js:
test.js:
像java的JUnit同樣,測試方法名要以」test」開頭,如:」testXXXX」
測試步驟:
安裝:http://code.google.com/p/js-test-driver/wiki/UsingTheEclipsePlugin
下載:http://code.google.com/p/js-test-driver/
參考:《測試驅動的JavaScript開發》
a) 簡介
FireUnit是一個基於Firebug的Javascript的單元測試框架。簡單說來,FireUnit給Firebug增長了一個標籤面板,並提供了一些簡單的JavaScript API來記錄和查看測試。
b) 優勢
簡單易用
c) 不足
功能很少,測試代碼經常寫在源碼裏,雖然能夠實時地看到效果,但耦合太強,不易清理
只運行在Firefox下
d) API
經常使用:
fireunit.ok(condition, message) true/false
fireunit.compare(actual, expect, message) 是否相等
fireunit.reCompare(regexp, string, message) 字符串是否與正則式匹配
fireunit.testDone(); 執行以上的測試,在Firebug的新標籤test中顯示結果
其它:
fireunit.runTests(「test2.html」, 「test3.html」) 一塊兒運行多個頁面的測試(每一個文件都含有一些獨立的測試)
fireunit.log(message) 打印log,但彷佛無論用
fireunit.id(id) 至關與document.getElementById
fireunit.click(element) 模擬觸發element的click事件
fireunit.focus(element) 模擬觸發element的focus事件
fireunit.mouseDown(element) 模擬觸發element的mouseDown事件
但看代碼明明是click事件
在FF下,只是執行了node.click();不會觸發onmousedown,只會觸發onclick。已驗證
fireunit.value(element) 修改element的value
fireunit.key(element, key) 模擬觸發element的key事件
例:
var input = document.getElementsByTagName(「input」)[0];
fireunit.key(input, 「a」);
沒用過:
fireunit.browser
fireunit.test
fireunit.forceHTTP
fireunit.registerPathHandler
fireunit.panel
fireunit.privilege
e) 使用
在FF下裝Firebug和FireUnit
直接在代碼裏寫測試,就像console.log();
參考:https://github.com/jeresig/fireunit/wiki/internals
http://hi.baidu.com/dearhwj/blog/item/1d1e0dbfc380f80219d81f35.html
QUnit框架簡單方便,測試界面直觀詳細
Jasmine功能強大,風格也簡單明瞭,符合前端開發者的編程習慣,推薦
JsTestDriver能夠和QUnit等框架結合,能夠同時測多個瀏覽器。但安裝複雜,只能在控制檯顯示,不友好,不夠清晰
FireUnit小巧靈活,加上Firebug的人氣應該很受歡迎
若是須要進行自動化測試, 多瞭解一下Jasmine和JsTestDriver,本文未深刻。
JavaScript單元測試框架詳細列表