前言
之前看了一些關於Jasmine的文章,後來在官網上看到了文檔,試着自己翻譯了一下,有問題的歡迎大家指正哈。
官網地址爲:Jasmine官方文檔
介紹
Jasmine是一種用於測試JavaScript代碼的行爲驅動開發框架。它不依賴於其他任何JavaScript框架。它也不需要一個DOM結構。它有一個乾淨、清晰的語法,所以你可以很輕易地書寫單元測試代碼。本指南是針對於Jasmine2.4.1版本。
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
單獨的分配
發佈頁面有下載獨立配置的鏈接,包括任何用於運行Jasmine的東西。在下載一個特定版本和壓縮包之後,打開SpecRunner.html
啓動所包含的specs。你會發現所需要的資源文件和他們各自的specs都在SpecRunner.html
裏的<head>
標籤裏引入。
Suites(測試程序組):describe
Suites直接調用Jasmine的全局函數describe()
並傳遞兩個參數:string
和function
。字符串表示被測試的Suites的名稱或者標題。而函數則是需要實現Suites的代碼塊。
Specs(測試程序體):it
Specs通過調用全局函數it()
來定義,跟describe類似的傳遞兩個參數:string
和function
。字符串是spec的標題,function
是測試程序或者測試代碼。Spec包括一個或多個測試代碼的狀態期望值(expectations)。Expectations在Jasmine中是一個要麼是真要麼是假的斷言。只有當spec中的斷言都爲真纔可以通過這個測試程序,否則測試返回failing。
It’s Just Functions
既然describe()
和it()
是函數體,它們就可以包含所有可執行的代碼去運行單體測試。JavaScript範圍規則適用,所以在describe()
聲明的變量在其Suites塊內都是可用的。
describe("A suite is just a function", function() {
var a;
it("and so is a spec", function() {
a = true;
expect(a).toBe(true);
});
});
Expectations:expect
Expectations是函數expect()
建立的,而expect()函數傳遞一個稱爲actual(實際值)的參數。Expectations鏈式的連接着傳遞參數expected(期望值)的Matcher函數。
Matchers
每一個Matchers執行實際值(actual
)和期望值(expected
)之間的布爾比較。它負責反饋Expectation是否爲真給Jasmine,而Jasmine根據這個布爾值比較來決定是否通過這個測試(Suites)。
任何Matcher都能通過在expect
調用Matcher前加上not
來實現一個否定的斷言(expect(a).not().toBe(false);
)。
代碼如下:
describe("The 'toBe' matcher compares with ===", function() {
it("and has a positive case", function() {
expect(true).toBe(true);
});
it("and can have a negative case", function() {
expect(false).not.toBe(true);
});
});
包含的Matchers
Jasmine有一套功能豐富的Matchers。當一個產品需要調用庫裏不包括的特殊的斷言時,也可以自己定義Matchers。
自定義Matchers
常見的Matchers使用代碼如下:
describe("Included matchers:", function() {
it("The 'toBe' matcher compares with ===", function() {
//toBe()類似於"==="
var a = 12;
var b = a;
expect(a).toBe(b);//通過
expect(a).not.toBe(null);//通過,因爲a!==null
});
describe("The 'toEqual' matcher", function() {
//toEqual()類似於"=="
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);//通過,兩個對象屬性和值都一樣
});
});
it("The 'toMatch' matcher is for regular expressions", function() {
//toMatch()用於匹配正則表達式
var message = "foo bar baz";
expect(message).toMatch(/bar/);//通過,參數可以是正則表達式
expect(message).toMatch("bar");//通過,參數可以是字符串
expect(message).not.toMatch(/quux/);//通過,因爲toMatch()匹配不到/quux/
});
it("The 'toBeDefined' matcher compares against `undefined`", function() {
//toBeDefined()判斷參數是否定義
var a = {
foo: "foo"
};
expect(a.foo).toBeDefined();
expect(a.bar).not.toBeDefined();
});
it("The `toBeUndefined` matcher compares against `undefined`", function() {
//toBeUndefined()判斷參數是否爲undefined
var a = {
foo: "foo"
};
expect(a.foo).not.toBeUndefined();
expect(a.bar).toBeUndefined();
});
it("The 'toBeNull' matcher compares against null", function() {
//toBeNull()判斷參數是否爲空
var a = null;
var foo = "foo";
expect(null).toBeNull();
expect(a).toBeNull();
expect(foo).not.toBeNull();
});
it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
//toBeTruthy()判斷參數轉化爲布爾值時是否爲true
var a, foo = "foo";
expect(foo).toBeTruthy();//通過,foo變量轉變爲true
expect(a).not.toBeTruthy();
});
it("The 'toBeFalsy' matcher is for boolean casting testing", function() {
//toBeFalsy()判斷參數轉化爲布爾值時是否爲false
var a, foo = "foo";
expect(a).toBeFalsy();
expect(foo).not.toBeFalsy();
});
it("The 'toContain' matcher is for finding an item in an Array", function() {
//toContain()判斷元素是否存在於數組內。不適用於對象
var a = ["foo", "bar", "baz"];
expect(a).toContain("bar");
expect(a).not.toContain("quux");
});
it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
//toBeLessThan()判斷實際值是否小於期望值
var pi = 3.1415926,
e = 2.78;
expect(e).toBeLessThan(pi);
expect(pi).not.toBeLessThan(e);
});
it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() {
//toBeGreaterThan()判斷實際值是否大於期望值
var pi = 3.1415926,
e = 2.78;
expect(pi).toBeGreaterThan(e);
expect(e).not.toBeGreaterThan(pi);
});
it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
//toBeCloseTo()數值比較時定義精度,先四捨五入後再比較
var pi = 3.1415926,
e = 2.78;
expect(pi).not.toBeCloseTo(e, 2);
expect(pi).toBeCloseTo(e, 0);
});
it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
//toThrow()判斷函數是否會拋出一個錯誤
var foo = function() {
return 1 + 2;
};
var bar = function() {
return a + 1;
};
expect(foo).not.toThrow();
expect(bar).toThrow();
});
it("The 'toThrowError' matcher is for testing a specific thrown exception", function() {
//toThrowError()判斷函數是否拋出一個特別的錯誤,以下四種都能夠通過測試
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");
});
});
使用fail函數手動失敗一個Spec
fail()
函數可以使一個spec測試失敗,可以傳遞一個失敗信息或者錯誤對象給函數作爲參數。例如:
describe("A spec using the fail function", function() {
var foo = function(x, callBack) {
if (x) {
callBack();
}
};
it("should not call the callBack", function() {
foo(false, function() {
fail("Callback has been called");
});
});
});
返回結果爲:
用describe
集合相關聯的測試程序體
describe()
函數將相關了的測試程序體集合起來,參數string
是爲集合起來的程序體所取的名字,並且是連接所有specs給每個spec取一個全稱。這個字符串使得能夠在一個大型Suite中找到這個spec。例如:
describe("A spec", function() {
it("is just a function, so it can contain any code", function() {
var foo = 0;
foo += 1;
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
var foo = 0;
foo += 1;
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
});
代碼向我們展示了一個describe()
函數就是幾個spec的集合,並且每個spec裏可以包括不止一個斷言。
安裝與拆卸
爲了幫助一個測試體能夠DRY(Don’t repeat yourself)任何重複的初始化和銷燬代碼,Jasmine提供了四個全局函數:beforeEach()
,afterEach()
,beforeAll()
,afterAll()
。
顧名思義,beforeEach()
函數在每一個spec調用時調用,先進行初始化操作,而afterEach()
則是在每一個spec調用之後調用。例如:
describe("A spec using beforeEach and afterEach", function() {
var foo = 0;
beforeEach(function() {
foo += 1;
});
afterEach(function() {
foo = 0;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
});
代碼中兩個spec的代碼有一點點不同,在上級作用域(describe
)定義的參數,在後面都可以對其進行修改賦值。每次spec執行時都初始化beforeEach()
的內容,之後在下一次繼續執行前,調用afterEach()
將變量重置。
beforeAll()
函數在describe()
函數運行時,並且在所有spec執行前只調用一次,而afterAll()
則是在所有spec結束後調用。這兩個函數可以用來加速那些需要花費昂貴的初始化和銷燬的測試單元。例如:
describe("A spec using beforeAll and afterAll", function() {
var foo;
beforeAll(function() {
foo = 1;
});
afterAll(function() {
foo = 0;
});
it("sets the initial value of foo before specs run", function() {
expect(foo).toEqual(1);
foo += 1;
});
it("does not reset foo between specs", function() {
expect(foo).toEqual(2);
});
});
代碼調用beforeAll()
在所有測試spec開始前對foo賦值爲1,在之後的測試改變了foo的值,但不再調用其初始化函數,在下一個spec內,foo等於2,。之後調用afterAll()
將foo賦值爲0。
然而,要小心使用beforeAll()
和afterAll()
函數,畢竟他們在每個spec之間沒有進行重置,很容易意外導致在你的測試體之間泄露狀態,最終使他們產生錯誤或無法通過測試。
this
關鍵字
除了在describe()
聲明變量外,另外一個在beforeEach()
,it()
,afterEach()
之間共享變量的方法是使用this關鍵字。每一個spec的beforeEach()
,it()
,afterEach()
擁有相同的在下個spec的beforeEach()
,it()
,afterEach()
執行之前會延遲置空的空對象this
。代碼如下:
describe("A spec", function() {
beforeEach(function() {
this.foo = 0;
});
it("can use the `this` to share state", function() {
expect(this.foo).toEqual(0);
this.bar = "test pollution?";
});
it("prevents test pollution by having an empty `this` created for the next spec", function() {
expect(this.foo).toEqual(0);
expect(this.bar).toBe(undefined);
});
});
代碼在每個spec之前聲明this.foo
並賦值,在其it()
函數裏能夠共享該變量。而在it()
函數裏聲明的this.bar
,在下一個spec執行時無法獲得,此時兩者的this
關鍵字所指向的並不一致。
嵌套describe代碼塊
describe
可以嵌套調用,而spec可以在任何層次都可以定義。類似於組成一個Suites的樹形結構。Spec執行前,Jasmine會按順序沿着這個樹形結構執行每個beforeEach()
函數。而執行結束後,Jasmine也將沿着樹執行afterEach()
函數。例如:
describe("A spec", function() {
var foo;
beforeEach(function() {
foo = 0;
foo += 1;
});
afterEach(function() {
foo = 0;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
describe("nested inside a second describe", function() {
var bar;
beforeEach(function() {
bar = 1;
});
it("can reference both scopes as needed", function() {
expect(foo).toEqual(bar);
});
});
});
代碼嵌套調用了describe()
函數,而上一層聲明的變量,在下一層Suites仍可以使用。
禁用Suites
通過調用xdescribe()
函數可以禁用Suites。代碼執行時,這些Suites以及裏面的任何spec都將跳過,因而他們的結果也不會出現在最終的輸出結果中。例如:
xdescribe("A spec", function() {
var foo;
beforeEach(function() {
foo = 0;
foo += 1;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
});
輸出結果如圖:
掛起Specs
被掛起的spec不會執行。但是他們的名字仍會顯示在pending的結果集中。
聲明掛起的方式有三種:
1. spec可以使用xit()
來聲明掛起;
2. spec聲明時不添加任何函數體也可以在結果中達到掛起的效果;
3. 如果你在spec體內的任何位置調用pending()函數,不管那個斷言通過與否,該spec都將被掛起。當Suites結束後,pending所傳遞的字符串將被作爲掛起的理由而顯示。
例如:
describe("Pending specs", function() {
xit("can be declared 'xit'", function() {
//使用xit()函數掛起
expect(true).toBe(false);
});
it("can be declared with 'it' but without a function");
//使用空函數體將spec掛起
it("can be declared by calling 'pending' in the spec body", function() {
expect(true).toBe(false);
pending('this is why it is pending'); //調用pending()將其掛起
});
});
最終顯示結果爲:
有興趣的同學可以查看下一篇文章:
Jasmine文檔(二)