什么是Jasmine
Jasmine是一個Javascript的BDD(Behavior-Driven Development)測試框架,不依賴任何其他框架。
如何使用Jasmine
從Github上(https://github.com/pivotal/jasmine/releases)下載所需的Jasmine版本。下載完成之后,直接打開SpecRunner.html即為Demo,除了引入Jasmine框架之外,只需引用自己所需測試的js文件以及Jasmine測試腳本引可。
圖1:
基本語法介紹
describe(string, function):可以理解為是一個測試集或者測試包(為了便於稱呼,我們本文中統一叫測試集,官方稱之為suite),主要功能是用來划分單元測試的,describe是可以嵌套使用的
- 參數string:描述測試包的信息
- 參數function:測試集的具體實現
it(string, function):測試用例(官方稱之為spec)
- 參數string:描述測試用例的信息
- 參數function:測試用例的具體實現
expect:斷言表達式
第一個Jasmine例子
Step 1:
我們先創建一個名為myFirstJasmineTest.js的文件。
Step 2:
在Html頁面中同時引用jasmine的框架文件與myFirstJasmineTest.js文件,可參考上面的圖1。
Step 3:
在myFirstJasmineTest.js文件中添加如下內容:
1 describe("My first Jasmine test", function() { 2 it("a spec with an expectation", function() { 3 expect(1).toBe(1); 4 expect(1===1).toBe(true); 5 expect('a').not.toBe('b'); 6 }); 7 8 it("an other spec in current suite", function() { 9 expect(true).toBe(true); 10 }); 11 }); 12 13 describe("My first Jasmine test", function() { 14 it("nothing", function() { 15 }); 16 });
打開Html頁面,Jasmine將自動執行用例:
圖2:
從上述例子中我們可以看出:
- 每個測試文件中可以包含多個describe
- 每個describe中可以包含多個it
- 每個it中可以包含多個expect
當然實際上,Jasmine還允許describe的嵌套使用,大家可以自行試試。
expect的使用
首先說一下,所有的expect都可以使用not表示否定的斷言。
toBe:基本類型判斷
it("toBe and not.toBe", function() { expect(1).toBe(1); expect('a').not.toBe('b'); });
toEqual: toEqual有兩種用法,對於基本的類型,toEqual相當於toBe
it("toEqual and not.toEqual for basic types", function(){ expect(1).toEqual(1); expect('a').not.toEqual('b'); })
toEqual還可以用來判斷對象:
it("toEqual and not.toEqual for objects", function(){ var o1 = { name: "Jack", age: 12 }; var o2 = { name: "Jack", age: 12 }; var o3 = { name: "Tom", age: 13 }; expect(o1).toEqual(o2); expect(o1).not.toEqual(o3); })
toMatch: 使用正則表達式判斷
it("toMatch and not.toMatch", function(){ var str = "Michael Jackson"; expect(str).toMatch(/michael/i); expect(str).not.toMatch(/tom/i); })
toBeDefine: 判斷是否是undefined
it("toBeDefined and not.toBeDefined", function(){ var student = { name: "Jack", age: 12 }; expect(student.name).toBeDefined(); expect(student.gender).not.toBeDefined(); })
toBeUndefined: 判斷是否是undefined,與toBeDefine相反
it("toBeUndefined and not.toBeUndefined", function(){ var student = { name: "Jack", age: 12 }; expect(student.gender).toBeUndefined(); expect(student.name).not.toBeUndefined(); })
toBeNull:判斷是否是null
it("toBeNull and not.toBeNull", function(){ var student = { name: "Jack", age: 12, deskmate: null }; expect(student.deskmate).toBeNull(); expect(student.name).not.toBeNull(); });
toBeTruthy:判斷是否能轉換成bool型,判斷的是否是True
it("toBeTruthy and not.toBeTruthy", function(){ var stu1; var stu2 = "Tom"; expect(true).toBeTruthy(); expect(stu2).toBeTruthy(); expect(stu1).not.toBeTruthy(); expect(undefined).not.toBeTruthy(); });
toBeTruthy:判斷是否能轉換成bool型,判斷的是否是False
it("toBeFalsy and not.toBeFalsy", function(){ var stu1; var stu2 = "Tom"; expect(true).not.toBeFalsy(); expect(stu1).toBeFalsy(); expect(stu2).not.toBeFalsy(); expect(undefined).toBeFalsy(); });
toContain: 判斷集合是否包含(可以是普通類型,和可以是對象)
it("toContain and not.toContain", function(){ var arrStr = ["Jack", "Tom", "Mary"]; var arrObj = [{name:"Jack",age:21}, {name:"Tom",age:22}]; expect(arrStr).toContain("Jack"); expect(arrStr).not.toContain("jack"); expect(arrObj).toContain({name:"Jack",age:21}); expect(arrObj).not.toContain({name:"jack",age:21}); });
toBeLessThan: 判斷值類型的大小,結果若小則為True(也可以判斷字符及字符串,以ascii碼的大小為判斷依據)
it("toBeLessThan and not.toBeLessThan", function(){ expect(1).toBeLessThan(1.1); expect("b").not.toBeLessThan("a"); });
toBeGreaterThan: 判斷值類型的大小,結果若大則為True,與toBeLessThan相反(也可以判斷字符及字符串,以ascii碼的大小為判斷依據)
it("toBeGreaterThan and not.toBeGreaterThan", function(){ expect(1).not.toBeGreaterThan(1.1); expect("b").toBeGreaterThan("a"); });
toBeCloseTo:判斷數字是否相似(第二個參數為小數精度,默認為2位)
it("toBeCloseTo and not.toBeCloseTo", function(){ var a = 1.1; var b = 1.5; var c = 1.455; var d = 1.459; expect(a).toBeCloseTo(b, 0); expect(a).not.toBeCloseTo(c, 1); expect(c).toBeCloseTo(d); });
toThrow: 判斷是否拋出異常
it("toThrow and not.toThrow", function(){ var foo = function() { return 1 + 2; }; var bar = function() { return a + 1; }; expect(foo).not.toThrow(); expect(bar).toThrow(); });
toThrowError: 判斷是否拋出了指定的錯誤
it("toThrowError and not.toThrowError", function() { var foo = function() { throw new TypeError("foo bar baz"); }; expect(foo).toThrowError("foo bar baz"); expect(foo).toThrowError(/bar/); expect(foo).toThrowError(TypeError); expect(foo).toThrowError(TypeError, "foo bar baz"); });
Setup和Teardown
Jasmine允許在執行測試集/測試用例的開始前/結束后做一些初始化/銷毀的操作。
Setup方法:
- beforeAll:每個suite(即describe)中所有spec(即it)運行之前運行
- beforeEach:每個spec(即it)運行之前運行
Teardown方法:
- afterAll:每個suite(即describe)中所有spec(即it)運行之后運行
- afterEach:每個spec(即it)運行之后運行
示例代碼:
1 (function(){ 2 var globalCount; 3 describe("Setup and Teardown suite 1", function() { 4 var suiteGlobalCount; 5 var eachTestCount; 6 7 beforeAll(function() { 8 globalCount = 0; // 試試注釋這行代碼,看看對運行結果的影響 9 suiteGlobalCount = 0; 10 eachTestCount = 0; 11 }); 12 13 afterAll(function() { 14 //globalCount = 0; // 試試取消這行代碼的注釋,看看對運行結果的影響 15 suiteGlobalCount = 0; 16 }); 17 18 beforeEach(function() { 19 globalCount++; 20 suiteGlobalCount++; 21 eachTestCount++; 22 }); 23 24 afterEach(function() { 25 eachTestCount = 0; 26 }); 27 28 it("Spec 1", function() { 29 expect(globalCount).toBe(1); 30 expect(suiteGlobalCount).toBe(1); 31 expect(eachTestCount).toBe(1); 32 }); 33 34 it("Spec 2", function() { 35 expect(globalCount).toBe(2); 36 expect(suiteGlobalCount).toBe(2); 37 expect(eachTestCount).toBe(1); 38 }); 39 }); 40 41 describe("Setup and Teardown suite 2", function() { 42 beforeEach(function() { 43 globalCount += 2; 44 }); 45 46 it("Spec 1", function() { 47 expect(globalCount).toBe(4); 48 }); 49 }); 50 })();
示例中的第一個describe,在beforeAll中初始化了各個計數變量,在beforeEach中設置每次執行it后,各個計數變量自增1,在afterAll中,重置了全局性的計數變量(嘗試取消afterAll中對globalCount的注釋,看看運行結果的變化),在afterEach中,重置了局部計數變量。
第二個describe,在beforeEach中對全局變量globalCount自增2,上述代碼中,第一個describe中afterAll中沒有對globalCount進行重置,因此執行完第一個describe后,globalCount的值為2,因此第二個describe的globalCount的初始值即為2。
在beforeEach/it/afterEach中,還可以使用this關鍵字定義變量,需要注意的是,使用this關鍵字聲明的變量,僅在beforeEach/it/afterEach這個過程中傳遞:
1 (function(){ 2 describe("Test 'this'", function() { 3 beforeEach(function() { 4 this.testCount = this.testCount || 0; 5 this.testCount++; 6 }); 7 8 afterEach(function() { 9 //this.testCount = 0; //無論是否有這行,結果是一樣的,因為this指定的變量只能在每個spec的beforeEach/it/afterEach過程中傳遞 10 }); 11 12 it("Spec 1", function() { 13 expect(this.testCount).toBe(1); 14 }); 15 16 it("Spec 2", function() { 17 expect(this.testCount).toBe(1); 18 }); 19 }); 20 })();
xdescribe/xit的使用
在實際項目中,需要由於發布的版本需要選擇測試用例包,xdescribe和xit能很方便的將不包含在版本中的測試用例排除在外。不過xdescribe和xit略有不同:
- xdescribe:該describe下的所有it將被忽略,Jasmine將直接忽略這些it,因此不會被運行
- xit:運行到該it時,掛起它不執行
1 (function(){ 2 xdescribe("Test xdescribe", function() { 3 it("Spec 1", function() { 4 expect(1).toBe(1); 5 }); 6 7 it("Spec 2", function() { 8 expect(2).toBe(2); 9 }); 10 }); 11 12 describe("Test xit", function() { 13 it("Spec 1", function() { 14 expect(1).toBe(1); 15 }); 16 17 xit("Spec 2", function() { 18 expect(2).toBe(1); 19 }); 20 21 xit("Spec 3", function() { 22 expect(3).toBe(3); 23 }); 24 }); 25 })();
運行結果:
篇幅和精力原因,關於Jasmine的入門學習,我們將在下一篇中繼續。
這里隨便扯幾句,最近由於新項目開始了,我在項目中同時需要擔任PM、BA、開發的角色(哦,對了,我忘了告訴你們,其實我是.net的程序員),確實非常累, 很多東西要學、要做。不過對於技術的熱愛(雖然技術不咋滴),我希望不管多忙多累,還是能在技術的道路上繼續走下去。大家如果覺得我寫的文章還可以,甚至能給你們帶來一些小小的收獲,希望大家頂一下,給我加加油。 好了,又到凌晨一點半了,我趕緊去睡了,明天還要去客戶現場呢。
參考資料
Jasmine: http://jasmine.github.io/
粉絲日志:http://blog.fens.me/nodejs-jasmine-bdd/