NG筆記4-單元測試
0 | 17,805 |
自動測試是AngularJS架構的重要一環,官方文件有專章討論單元測試,程式庫則有ngMock提供單元測試所時的DI及Mocking(假物件模擬)支援。本篇將討論如何在Visual Studio 2013對Controller及ViewModel進行單元測試。
開始之前,Visual Studio要先安裝Chutzpah(發音類似"鬍子爸" XD)延伸套件,流浪小風有篇詳細介紹,在此不多贅述,直接切入Angular相關部分。我們就拿NG筆記2的簡單範例來練兵,為它建立單元測試。
首先,在Solution新增一個Class Libaray專案,並從NuGet安裝jasmine.js。(Jasmine是一套BDD精神的JavaScript測試Framework,AngularJS的單元測試及End-to-End(E2E)測試都用Jasmine寫測試腳本)
接著,在專案新增Tests資料夾,專門用來放置測試Script,測試檔可使用*.tests.js格式命名。整體的專案結構如下:
注意,T1.tests.js需以<reference>引用來自Web專案裡的JavaScript,Chutzpah會依照reference載入所需JS執行單元測試。如果希望編寫Jasmine Script階段能有完整Intellisense,可以連angular-mock.js、jasmine.js也一併納入參考。
T1.tests.js範例如下:
//測試集
describe("SampleApp測試", function () {
var scope, controller;
//beforeEach()內為每回測試前執行的程序
beforeEach(function () {
//透過ngMock註冊sampleApp的相關設定,供隨後DI產生模組
module("sampleApp");
});
//子測試集
describe("mainCtrl測試", function () {
//每個Spec(下方的it())執行前使用ngMock inject()產生Controller及Scope
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller("mainCtrl", {
"$scope": scope
});
}));
//Spec 1
it("初始firstName等於'Jeffrey'", function () {
expect(scope.model.firstName).toBe("Jeffrey");
});
//Spec 2
it("fullName()由firstName及lastName組成", function () {
scope.model.firstName = "Darkthread";
scope.model.lastName = "Run"
expect(scope.model.fullName()).toBe("Darkthread Run");
});
});
});
上述程式中的describe(), it(), expect()是Jasmine指令,Jasmine的指令沒幾個,官方文件看一遍就能上手,甚至不讀文件,直接看程式碼也不難望文生義。程式裡的module()、inject()是ngMock提供的功能,處理Module、Controller載入及Scope建立,透過DI可依需要注入假物件,而ngMock提供的$httpBackend假物件能模擬AJAX呼叫行為,確保前端程式在沒有伺服器時能也能單獨執行,這是形成單元測試的重要條件。每個it()通常用來印證一條"規格"、beforeEach()內的程式在每次跑it()前會先被執行,備妥測試環境。
Visual Studio安裝Chutzpah套件後,在T1.tests.js按右鍵可選擇直接執行測試或開啟瀏覽器檢視測試報表: (瀏覽器檢視時還能用Dev Tools偵錯,實務上不可或缺)
或者用Test Explorer選取測試執行也成:
介紹完用tests.js跑測試,該來談談TypeScript時代的單元測試,看看如何用TypeScript寫Jasmine測試?
首先在測試專案加入Jasmine的TypeScript定義檔:
新增TypeScript測試檔(T2.tests.ts),內容如下:
/// <reference path="../scripts/typings/jasmine/jasmine.d.ts" />
/// <reference path="../../web/scripts/typings/angularjs/angular-mocks.d.ts" />
/// <reference path="../../web/scripts/vms/user.ts" />
/// <reference path="../../web/scripts/apps/sampleapp.ts" />
/// <chutzpah_reference path="../../web/scripts/boo.js" />
/// <chutzpah_reference path="../../web/scripts/angular.js" />
/// <chutzpah_reference path="../../web/scripts/angular-mocks.js" />
/// <chutzpah_reference path="../../web/scripts/vms/users.js" />
/// <chutzpah_reference path="../../web/scripts/apps/sampleApp.js" />
describe("[TS]SampleApp測試", () => {
var scope, controller;
beforeEach(module("sampleApp"));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller("mainCtrl", {
"$scope": scope
});
}));
it("初始firstName等於'Jeffrey'", () => {
expect(scope.model.firstName).toBe("Jeffrey");
});
it("fullName()由firstName及lastName組成", () => {
var model: User = scope.model;
model.firstName = "Darkthread";
model.lastName = "Run"
expect(model.fullName()).toBe("Darkthread Run");
});
});
重點在上方的參考宣告。測試TypeScript會用到Jasmine的describe(), beforeEach(),也會用到ngMock的module(), inject(),因此要納入jasmine.d.ts及angular-mocks.d.ts參考才能編譯,而測試要用到的Controller及ViewModel TypeScript,也需以<reference>加入參考。
TypeScript測試檔跟JavaScript測試檔的最大不同,在於<reference>加入*.ts是要讓TypeScript順利編譯,不等於執行期間要載入的JS項目,因此需要另外定義<chutzpah_reference>,取代原本JavaScript <reference>,讓Chutzpah知道要跑測試時載入哪些JavaScript檔案。所以在撰寫TypeScript測試檔時,除了參考TS,不要忘了用<chutzpah_reference>將需要的JS都列上去。
T2.tests.ts完成後,透過右鍵Run Tests及Test Exploer就能執行TypeScript所寫的測試檔。
提醒: 使用TypeScript時,不建議使用右鍵"Open in browser"功能,原因是Chutzpah每次執行會重新編譯TypeScript,產生類似_Chutzpah.53.sampleapp.js的暫存檔,為配合瀏覽器開啟檔案不會自動刪除,久而久之就會產生一大堆暫存檔,要改善這個問題,建議使用自訂TestRunner網頁取代。
Comments
Be the first to post a comment