Jasmine 爲 JavaScript 提供了 TDD (測試驅動開發)的框架,對於前端軟件開發提供了良好的質量保證,這裏對 Jasmine 的配置和使用作一個說明。css
目前,Jasmine 的最新版本是 2.3 版,這裏以 2.3 版進行說明。網上已經有一些關於 Jasmine 的資料,可是,有些資料比較久遠,已經與現有版本不一致。因此,這裏特別以最新版進行說明。html
官網地址:http://jasmine.github.io/前端
官網文檔地址:http://jasmine.github.io/2.3/introduction.htmlnode
下載地址:https://github.com/jasmine/jasmine/releasesgit
在 GitHub 上提供了獨立版本 jasmine-standalone-2.3.4.zip 和源碼版本,若是使用的話,直接使用 standalone 版本便可。github
解壓以後,能夠獲得以下所示的文件結構。正則表達式
其中,lib 中是 Jasmine 的實現文件,在 lib/jasmine-2.3.4 文件夾中,能夠看到以下的文件。express
打開最外層的 SpecRunner.html ,這是一個 Jasmine 的模板,其中提供了測試的示例,咱們能夠在使用中直接套用這個模板。其中的內容爲:數組
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Jasmine Spec Runner v2.3.4</title> <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.3.4/jasmine_favicon.png"> <link rel="stylesheet" href="lib/jasmine-2.3.4/jasmine.css"> <script src="lib/jasmine-2.3.4/jasmine.js"></script> <script src="lib/jasmine-2.3.4/jasmine-html.js"></script> <script src="lib/jasmine-2.3.4/boot.js"></script> <!-- include source files here... --> <script src="src/Player.js"></script> <script src="src/Song.js"></script> <!-- include spec files here... --> <script src="spec/SpecHelper.js"></script> <script src="spec/PlayerSpec.js"></script> </head> <body> </body> </html>
能夠看到其中引用了 lib/jasmine-2.3.4/jasmine.js, lib/jasmine-2.3.4/jasmine- html.js 和 lib/jasmine-2.3.4/boot.js 三個系統文件,其中 boot.js 是網頁狀況下的啓動文件,在 張丹 的 jasmine行爲驅動,測試先行 這篇文章中,要寫一個 report.js 的啓動腳本,這裏已經不用了,直接使用 boot.js 就能夠。瀏覽器
頁面下面引用的 src/Player.js 和 src/Song.js 是咱們的測試對象,而 spec/SpecHelper.js 和 spec/PlayerSpec.js 則是兩個對應的測試文件,測試用例就定義在 spec 中。
咱們仍是直接看示例,
一個是 Song.js,這裏定義了一個 Song 的類,經過原型定義了一個persistFavoriteStatus 實例方法,注意,這裏尚未實現,若是調用則會拋出異常。腳本以下。
function Song() { } Song.prototype.persistFavoriteStatus = function(value) { // something complicated throw new Error("not yet implemented"); };
另一個是 player.js,定義了 Player 類,定義了一個歌手,經過原型定義了 play, pause, resume 和 makeFavorite 實例方法。對象有一個 isPlaying 的狀態,其中 resume 尚未完成。
function Player() { } Player.prototype.play = function(song) { this.currentlyPlayingSong = song; this.isPlaying = true; }; Player.prototype.pause = function() { this.isPlaying = false; }; Player.prototype.resume = function() { if (this.isPlaying) { throw new Error("song is already playing"); } this.isPlaying = true; }; Player.prototype.makeFavorite = function() { this.currentlyPlayingSong.persistFavoriteStatus(true); };
下面看測試的定義,具體測試的說明,直接加在註釋中。
describe("Player", function() { var player; var song; beforeEach(function() { player = new Player(); song = new Song(); }); // 檢測正在歌手進行的歌曲確實是指定的歌曲 it("should be able to play a Song", function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song); //demonstrates use of custom matcher expect(player).toBePlaying(song); }); // 進行測試的分組,這裏測試暫停狀態 describe("when song has been paused", function() { beforeEach(function() { player.play(song); player.pause(); }); // isPlaying 的狀態檢測 it("should indicate that the song is currently paused", function() { expect(player.isPlaying).toBeFalsy(); // demonstrates use of 'not' with a custom matcher // expect(player).not.toBePlaying(song); }); // 恢復 it("should be possible to resume", function() { player.resume(); expect(player.isPlaying).toBeTruthy(); expect(player.currentlyPlayingSong).toEqual(song); }); }); // demonstrates use of spies to intercept and test method calls // 使用 spyOn 爲對象建立一個 mock 函數 it("tells the current song if the user has made it a favorite", function() { spyOn(song, 'persistFavoriteStatus'); player.play(song); player.makeFavorite(); expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); }); //demonstrates use of expected exceptions // 異常檢測 describe("#resume", function() { it("should throw an exception if song is already playing", function() { player.play(song); expect(function() { player.resume(); }).toThrowError("song is already playing"); }); }); });
使用瀏覽器直接打開 SpenRunner.html 看到的結果
能夠看到測試都經過了。
若是咱們將第一個測試 expect(player.currentlyPlayingSong).toEqual(song); 改爲 expect(player.currentlyPlayingSong).toEqual( 1 );
測試通不過,顯示會變成這樣。
describe 用來對測試用例進行分組,分組能夠嵌套,每一個分組能夠有一個描述說明,這個說明將會出如今測試結果的頁面中。
describe("Player", function() { describe("when song has been paused", function() {
而 it 就是測試用例,每一個測試用例有一個字符串的說明,匿名函數內就是測試內容。
// 檢測正在歌手進行的歌曲確實是指定的歌曲 it("should be able to play a Song", function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song); });
測試結果的斷言使用 expect 進行,函數內提供測試的值,toXXX 中則是指望的值。
上面的測試使用 toEqual 進行相等斷言判斷。
示例中還出現了 beforeEach。
var player; var song; beforeEach(function() { player = new Player(); song = new Song(); });
顧名思義,它表示在本組的每一個測試以前須要進行的準備工做。在咱們這裏的測試中,總要用到 player 和 song 這兩個對象實例,使用 forEach 保證在每一個測試用例執行以前,從新對這兩個對象進行了初始化。
afterEach 會在每個測試用例執行以後執行。
除了系統定義的 toEqual 等等斷言以外,也可使用自定義的斷言,在上面的示例中就出現了 toBePlaying 斷言。
//demonstrates use of custom matcher expect(player).toBePlaying(song);
這個自定義的斷言定義在 SpecHelper.js 文件中。
beforeEach(function () { jasmine.addMatchers({ toBePlaying: function () { return { compare: function (actual, expected) { var player = actual; return { pass: player.currentlyPlayingSong === expected && player.isPlaying }; } }; } }); });
其中調用了 jasmine 的 addMatchers 函數進行定義,原來這裏不叫斷言,稱爲 matcher ,也就是匹配器。
斷言是一個函數,返回一個對象,其中有一個 compare 的函數,這個函數接收兩個參數,第一個是實際值,第二個爲指望的值。具體的斷言邏輯本身定義,這裏比較歌手演唱的對象是否爲咱們傳遞的對象,而且歌手的狀態爲正在表演中。
斷言函數須要返回一個對象,對象的 pass 屬性爲一個 boolean 值,表示是否經過。
4.1 toEqual
深相等,對於對象來講,會比較對象的每一個屬性。對於數組來講,會比較數組中每一個元素。
describe("The 'toEqual' matcher", function() { it("works for simple literals and variables", function() { var a = 12; expect(a).toEqual(12); }); it("should work for objects", function() { var foo = { a: 12, b: 34 }; var bar = { a: 12, b: 34 }; expect(foo).toEqual(bar); }); });
4.2 toBe
對於對象,引用相等。對於值,值相等。
pass: actual === expected
例如
it("and has a positive case", function() { expect(true).toBe(true); });
4.3 toBeTruthy
是否爲真。
it("The 'toBeTruthy' matcher is for boolean casting testing", function() { var a, foo = "foo"; expect(foo).toBeTruthy(); expect(a).not.toBeTruthy(); });
4.4 toBeFalsy
是否爲假。
it("The 'toBeFalsy' matcher is for boolean casting testing", function() { var a, foo = "foo"; expect(a).toBeFalsy(); expect(foo).not.toBeFalsy(); });
4.5 toBeDefined
是否認義過
it("creates spies for each requested function", function() { expect(tape.play).toBeDefined(); expect(tape.pause).toBeDefined(); expect(tape.stop).toBeDefined(); expect(tape.rewind).toBeDefined(); });
4.6 toBeUndefined
沒有定義
it("The `toBeUndefined` matcher compares against `undefined`", function() { var a = { foo: "foo" }; expect(a.foo).not.toBeUndefined(); expect(a.bar).toBeUndefined(); });
4.7 toBeNull
it("The 'toBeNull' matcher compares against null", function() { var a = null; var foo = "foo"; expect(null).toBeNull(); expect(a).toBeNull(); expect(foo).not.toBeNull(); });
4.9 toBeGreaterThan
it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() { var pi = 3.1415926, e = 2.78; expect(pi).toBeGreaterThan(e); expect(e).not.toBeGreaterThan(pi); });
4.10 toBeLessThan
it("The 'toBeLessThan' matcher is for mathematical comparisons", function() { var pi = 3.1415926, e = 2.78; expect(e).toBeLessThan(pi); expect(pi).not.toBeLessThan(e); });
4.11 toBeCloseTo
it("The 'toBeCloseTo' matcher is for precision math comparison", function() { var pi = 3.1415926, e = 2.78; expect(pi).not.toBeCloseTo(e, 2); expect(pi).toBeCloseTo(e, 0); });
4.12 toContain
集合中是否包含。
it("The 'toContain' matcher is for finding an item in an Array", function() { var a = ["foo", "bar", "baz"]; expect(a).toContain("bar"); expect(a).not.toContain("quux"); });
4.13 toMatch
正則表達式的匹配
it("The 'toMatch' matcher is for regular expressions", function() { var message = "foo bar baz"; expect(message).toMatch(/bar/); expect(message).toMatch("bar"); expect(message).not.toMatch(/quux/); });
4.14 toThrow
檢測是否拋出異常
it("The 'toThrow' matcher is for testing if a function throws an exception", function() { var foo = function() { return 1 + 2; }; var bar = function() { return a + 1; }; expect(foo).not.toThrow(); expect(bar).toThrow(); });
4.15 toHaveBeenCalled
4.16 toHaveBeenCalledWith
是否調用過。
describe("A spy", function() { var foo, bar = null; beforeEach(function() { foo = { setBar: function(value) { bar = value; } }; spyOn(foo, 'setBar'); foo.setBar(123); foo.setBar(456, 'another param'); }); it("tracks that the spy was called", function() { expect(foo.setBar).toHaveBeenCalled(); }); it("tracks all the arguments of its calls", function() { expect(foo.setBar).toHaveBeenCalledWith(123); expect(foo.setBar).toHaveBeenCalledWith(456, 'another param'); }); it("stops all execution on a function", function() { expect(bar).toBeNull(); }); });