JavaScript 單元測試框架:Jasmine 初探

selena 9年前發布 | 11K 次閱讀 JavaScript開發

來自: http://www.ibm.com/developerworks/cn/web/1404_changwz_jasmine/


簡介

隨著互聯網浪潮的逐漸興起,各種基于互聯網的云戰略也不斷涌現,各個公司對云平臺的理解和實現不盡相同,而云+端的模式越來越多受到關注。其中的端可以理解為終端用戶手中的各種終端,包括 PC、手機、平板等不一而足。 而越來越多的用戶愿意在自己的設備上使用輕量級的基于瀏覽器的應用。這類應用的安裝部署可以是通過插件的方式安裝,也有可能是直接以網頁的形式訪問而無需安裝,相對于富客戶端的下載安裝,對用戶來說更加簡單方便,用戶體驗也更好。

這類應用對開發人員來說,需要一些互聯網相關的技術,其中必不可少 HTML CSS 和 JavaScript 技術。而 JavaScript 作為一種客戶端腳本語言,和傳統編程語言 Cpp、Java 等相比,沒有諸如 Eclipse、Visual Studio 等集成開發調試環境,其調試和測試是對開發人員都是一項挑戰。

目前 JS 單元測試框架有豐富的選擇,比如 Buster.js、TestSwarm、JsTestDriver 等。而 Jasmine 作為流行的 JavaScript 測試工具,很輕巧只有 20K 左右,而功能豐富,讓我們可以容易的寫出清晰簡潔的針對項目的測試用例。對基于 JavaScript 開發的項目來說,是一款不錯的測試框架選擇。


 

搭建環境

獲取安裝包

可以在開源社區網站下載最新的 Jasmine 安裝包, 目前的 Standalone 的最新版本是 1.3.0. 下載地址: https://github.com/pivotal/jasmine/downloads

配置安裝

下載后的.zip 文件包解壓縮,如下的目錄結構:

圖 0.目錄結構

目錄結構

其中 lib 文件夾中包含 Jasmine 的源代碼。采用如下相對路徑可以包含 Jasmine,進而開發基于 Jasmine 的測試用例。

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-1.3.0/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.0/jasmine.css">
  <script type="text/javascript" src="lib/jasmine-1.3.0/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-1.3.0/jasmine-html.js"></script>

spec 和 src 和 SpecRunner.html 是 Jasmine 的一個完整示例,用瀏覽器打開 SpecRunner.html,即可看到執行的結果。


回頁首

基本概念

describe

describe 是 Jasmine 的全局函數,作為一個 Test Suite 的開始,它通常有 2 個參數:字符串和方法。字符串作為特定 Suite 的名字和標題。方法是包含實現 Suite 的代碼。

清單 1.測試用例
describe("This is an exmaple suite", function() {
  it("contains spec with an expectation", function() {
    expect(true).toBe(true);
    expect(false).toBe(false);
    expect(false).not.toBe(true);
  });
});

Specs

Specs 通過調用 it 的全局函數來定義。和 describe 類似,it 也是有 2 個參數,字符串和方法。每個 Spec 包含一個或多個 expectations 來測試需要測試代碼。

Jasmine 中的每個 expectation 是一個斷言,可以是 true 或者 false。當每個 Spec 中的所有 expectations 都是 true,則通過測試。有任何一個 expectation 是 false,則未通過測試。而方法的內容就是測試主體。

JavaScript 的作用域的規則適用,所以在 describe 定義的變量對 Suite 中的任何 it 代碼塊都是可見的。

清單 2.測試用例
describe("Test suite is a function.", function() {
  var gVar;

  it("Spec is a function.", function() {
    gVar = true;
    expect(gVar).toBe(true);
  });

  it("Another spec is a function.", function() {
    gVar = false;
    expect(gVar).toBe(false);
  });

});

Expectations

Expectations 是由方法 expect 來定義,一個值代表實際值。另外的匹配的方法,代表期望值。

清單 3.測試用例
describe("This is an exmaple suite", function() {
  it("contains spec with an expectation", function() {
    var num = 10;
    expect(num).toEqual(10);
  });
});

以上代碼可以在附件中的 List.html 運行,結果見下圖:

圖 1.測試用例結果

測試用例結果

總結

describe 方法用來組織相關的 Spec 集合。string 參數作為 Spec 集合的名字,會和其中的 Spec 連接組成 Spec 的完整名字。這樣在一個大的 suite 中可以更容易找到某個 Spec。如果給它們命名適當,Specs 讀起來是一個典型的 BDD 樣式的句子。

Spec 是作為測試主體,Suite 是一個或多個 Spec 的集合。

describe 和 it 代碼塊中都是方法,可以包含任何可執行的代碼來實現測試。而方法的內容就是 Suites。


 

常見用法

Matchers

每個 Matchers 實現一個布爾值,在實際值和期望值之間比較。它負責通知 Jasmine,此 expectation 是真或者假。然后 Jasmine 會認為相應的 spec 是通過還是失敗。

任何 Matcher 可以在調用此 Matcher 之前用 not 的 expect 調用,計算負值的判斷。

清單 4.測試用例
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);
  });
});

Included Matchers

Jasmine 有很多的 Matchers 集合。下面的例子中舉例說明一些常用的 Matchers。另外當項目需要特定的判斷,而沒有包含在 Jasmine 的 Matchers 時,也可以通過寫定制的 Matchers 來實現.

清單 5.測試用例
describe("Included matchers:", function() {

  it("The 'toBe' Matcher", function() {
    var a = 3.6;
    var b = a;

    expect(a).toBe(b);
    expect(a).not.toBe(null);
  });

  describe("The 'toEqual' matcher", function() {

    it("works for simple literals and variables", function() {
      var a = "varA";
      expect(a).toEqual("varA");
    });

    it("Work for objects", function() {
      var obj = {
        a: 1,
        b: 4
      };
      var obj2 = {
        a: 1,
        b: 4
      };
      expect(obj).toEqual(obj2);
    });
  });

  it("The 'toBeDefined' matcher ", function() {
    var obj = {
      defined: 'defined'
    };

    expect(obj.defined).toBeDefined();
    expect(obj.undefined).not.toBeDefined();
  });

});

其他的 Matchers 還有:

toBe()
toNotBe()
toBeDefined()
toBeUndefined()
toBeNull()
toBeTruthy()
toBeFalsy()
toBeLessThan()
toBeGreaterThan()
toEqual()
toNotEqual()
toContain()
toBeCloseTo()
toHaveBeenCalled()
toHaveBeenCalledWith()
toMatch()
toNotMatch()
toThrow()

Setup and Teardown

為了使某個測試用例干凈的重復 setup 和 teardown 代碼, Jasmine 提供了全局的 beforeEach 和 afterEach 方法。正像其名字一樣,beforeEach 方法在 describe 中的

每個 Spec 執行之前運行,afterEach 在每個 Spec 調用后運行。

這里的在同一 Spec 集合中的例子有些不同。測試中的變量被定義為全局的 describe 代碼塊中,用來初始化的代碼被挪到 beforeEach 方法中。afterEach 方法在繼續前重置這些變量。

清單 6.測試用例
describe("An example of setup and teardown)", function() {
  var gVar;

  beforeEach(function() {
    gVar = 3.6;
    gVar += 1;
  });

  afterEach(function() {
    gVar = 0;
  });

  it("after setup, gVar has new value.", function() {
    expect(gVar).toEqual(4.6);
  });

  it("A spec contains 2 expectations.", function() {
    gVar = 0;
    expect(gVar).toEqual(0);
    expect(true).toEqual(true);
  });
});

嵌套代碼塊

describe 可以嵌套, Specs 可以定義在任何一層。這樣就可以讓一個 suite 由一組樹狀的方法組成。在每個 spec 執行前,Jasmine 遍歷樹結構,按順序執行每個 beforeEach 方法。Spec 執行后,Jasmine 同樣執行相應的 afterEach。

清單 7.測試用例
describe("A spec", function() {
  var gVar;

  beforeEach(function() {
    gVar = 3.6;
    gVar += 1;
  });

  afterEach(function() {
    gVar = 0;
  });

  it("after setup, gVar has new value.", function() {
    expect(gVar).toEqual(4.6);
  });

  it("A spec contains 2 expectations.", function() {
    gVar = 0;
    expect(gVar).toEqual(0);
    expect(true).toEqual(true);
  });

  describe("nested describe", function() {
    var tempVar;

    beforeEach(function() {
      tempVar = 4.6;
    });

    it("gVar is global scope, tempVar is this describe scope.", function() {
      expect(gVar).toEqual(tempVar);
    });
  });
});

跳過測試代碼塊

Suites 和 Specs 分別可以用 xdescribe 和 xit 方法來禁用。運行時,這些 Suites 和 Specs 會被跳過,也不會在結果中出現。這可以方便的在項目中可以根據需要來禁用隱藏某些測試用例。

清單 8.測試用例
xdescribe("An example of xdescribe.", function() {
  var gVar;

  beforeEach(function() {
    gVar = 3.6;
    gVar += 1;
  });

  xit(" and xit", function() {
    expect(gVar).toEqual(4.6);
  });
});

以上代碼可以在附件中的 List.html 運行,結果見下圖:

圖 2.測試用例結果

測試用例結果


 

與其他工具的集成

Karma

在 Java 中,用 JUnit 做單元測試, 用 Maven 進行自動化單元測試;

同樣相對應的 JS 中,則可以用 Jasmine 做單元測試,用 Karma 自動化完成單元測試。

Karma 作為 JavaScript 測試執行過程管理工具,可用于測試所有主流 Web 瀏覽器。下面簡單介紹一下 Karma 與 Jasmine 的集成。

首先,下載安裝 Karma。

初始化 karma 配置文件 karma.conf.js。

安裝集成包 karma-jasmine。

修改 karma.conf.js 配置文件。

需要修改:files 和 exclude 變量。其中 autoWatch 設置為 true,這樣如果修改測試文件并保存后,Karma 會檢測到然后自動執行。

module.exports = function (config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine'],
        files: ['*.js'],
        exclude: ['karma.conf.js'],
        reporters: ['progress'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['Chrome'],
        captureTimeout: 60000,
        singleRun: false
    });
};

啟動 karma,自動執行單元測試。

F:\Projects\karma>karma start karma.conf.js

另外,Jasmine 也可以與持續集成工具 Jenkins 進行集成。


 

一個 Jasmine 的完整例子

Jasmine 在 JavaScript 中編譯,必須被包含在一個 JS 環境中,比如一個 web 網頁,來運行。JavaScript 被包含,通過一個<script>標簽,然后所有以上的 specs 可以通過 Jasmine 計算和記錄。這樣 Jasmine 可以運行所有這些 specs。此頁面被認為是一個"runner"運行者。

下面通過一個具體的例子來介紹通過 runner 怎樣執行 Jasmine 的 suite。

首先,創建 HTMLReporter,Jasmine 調用它,來提供每個 Spec 和 Suite 的結果。

報告負責展現結果給用戶。

代理為報告過濾 specs。允許點擊某個結果中的 suites 或者 specs 來只運行 suite 的子集合。

當頁面完成加載時運行所有的測試-然后確認運行任何之前的 onload 句柄。

見下面清單 9,完整代碼示例見附件。

清單 9 .代碼示例
  <script type="text/javascript">    
    var jasmineEnv = jasmine.getEnv();
        var htmlReporter = new jasmine.HtmlReporter();

        jasmineEnv.addReporter(htmlReporter);
      
    jasmineEnv.specFilter = function(spec) {
        return htmlReporter.specFilter(spec);
        };    

    window.onload = function() {
        jasmineEnv.execute();
        };
  </script>

完整例子可以在附件中的 Reporter.html 運行,結果見下圖:

圖 3.測試用例結果

測試用例結果


 

結語

通過本文的介紹,我們可以了解 Jasmine 的一些基本概念和用法,為組織項目的測試打下基礎,為項目代碼的可靠性和穩定性提供保證,并介紹了 Jasmine 和其他框架的集成。Jasmine 的一些相對高級的用法和技巧,會在后續的文章中進行介紹。

 本文由用戶 selena 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!