轉自:http://www.ngnice.com/posts/dc4b032b537ae0javascript
當ng
項目愈來愈大的時候,單元測試就要提上日程了,有的時候團隊是以測試先行,有的是先實現功能,後面再測試功能模塊,這個各有利弊,今天主要說說利用karma
和jasmine
來進行ng
模塊的單元測試.php
karma
是一個單元測試的運行控制框架,提供以不一樣環境來運行單元測試,好比chrome
,firfox
,phantomjs
等,測試框架支持jasmine
,mocha
,qunit
,是一個以nodejs
爲環境的npm
模塊.html
安裝測試相關的npm
模塊,通常的運行karma
的話只須要下面兩個npm
命令,第二個是一個測試框架,karma默認的。java
安裝karma
的時候也會自動的安裝一些經常使用的模塊node
而後一個典型的運行框架一般都須要一個配置文件,在karma
裏能夠是一個karma.conf.js
,裏面的代碼是一個nodejs
風格的,一個普通的例子以下
karmakarma.conf.jsnodejs
// Karma configuration // Generated on Wed Apr 08 2015 09:26:37 GMT+0800 (中國標準時間) /*global module:true*/ (function () { 'use strict'; module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine'], files: [ "lib/angular.js", "lib/*.js", "js/*.js", "test/*.js" ], exclude: [ "lib/angular-scenario.js" ], urlRoot: "/base", preprocessors: {}, reporters: ['progress'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], singleRun: false }); }; }());
karma
就講到這裏,想了解更多關於它的信息能夠,點擊這裏git
jasmine是一個行爲驅動開發的測試框架,不依賴任何js
框架以及dom
,是一個很是乾淨以及友好API的測試庫.angularjs
下面簡單的以一個例子來講明它的用法github
定義一個測試文件命令爲test.js
chrome
(function () { "use strict"; describe("A suite of basic functions", function () { it("contains spec with an expectation", function () { expect(true).toBe(true); }); var a; it("and so is a spec", function () { a = true; expect(a).toBe(true); }); }); }());
上面的例子來自於官網,這裏只說下幾個重要的API,更多的用法請,點擊這裏express
首先任何一個測試用例以describe
函數來定義,它有兩參數,第一個用來描述測試大致的中心內容,第二個參數是一個函數,裏面寫一些真實的測試代碼
it
是用來定義單個具體測試任務,也有兩個參數,第一個用來描述測試內容,第二個參數是一個函數,裏面存放一些測試方法
expect
主要用來計算一個變量或者一個表達式的值,而後用來跟指望的值比較或者作一些其它的事件
beforeEach
與afterEach
主要是用來在執行測試任務以前和以後作一些事情,上面的例子就是在執行以前改變變量的值,而後在執行完成以後重置變量的值
最後要說的是,describe
函數裏的做用域跟普通JS同樣都是能夠在裏面的子函數裏訪問的
想要運行上面的測試例子能夠經過karar
來運行,命令例子以下
karma start karma.conf.js
下面咱們重點的說說ng
裏的控制器,指令,服務模塊的單元測試.
由於ng
自己框架的緣由,模塊都是經過di
來加載以及實例化的,因此爲了方便配合jasmine
來編寫測試腳本,因此官方提供了angular-mock.js
的一個測試工具類來提供模塊定義,加載,注入等.
下面說說ng-mock
裏的一些經常使用方法
module
是用來配置inject
方法注入的模塊信息,參數能夠是字符串,函數,對象,能夠像下面這樣使用
beforeEach(module('myApp.filters')); beforeEach(module(function($provide) { $provide.value('version', 'TEST_VER'); }));
它通常用在beforeEach
方法裏,由於這個能夠確保在執行測試任務的時候,inject
方法能夠獲取到模塊配置
inject
是用來注入上面配置好的ng
模塊,方面在it
的測試函數裏調用,常見的調用例子以下
describe("ng-mock test", function () { //提早加載模塊 beforeEach(module('test')); //測試value,function中的參數是自動注入 it('should provide a version', inject(function (author, version) { expect(version).toEqual('1.0.0'); expect(author).toEqual('wwy'); })); //用$provide修改value值 it('should override a version and test the new version is injected', function () { module(function ($provide) { $provide.value('version', 'overridden'); }); inject(function (version) { expect(version).toEqual('overridden'); }); }); });
上面是官方提供的一些inject
例子,代碼很好看懂,其實inject
裏面就是利用angular.inject
方法建立的一個內置的依賴注入實例,而後裏面的模塊注入跟普通ng
模塊裏的依賴處理是同樣的
簡單的介紹完ng-mock
以後,下面咱們分別以控制器,指令,過濾器來編寫一個簡單的單元測試。
定義一個簡單的控制器
var myApp = angular.module('myApp',[]); myApp.controller('MyController', function($scope) { $scope.spices = [{"name":"pasilla", "spiciness":"mild"}, {"name":"jalapeno", "spiciness":"hot hot hot!"}, {"name":"habanero", "spiciness":"LAVA HOT!!"}]; $scope.spice = "hello feenan!"; });
而後咱們編寫一個測試腳本
describe('myController function', function () { describe('myController', function () { var $scope; beforeEach(module('myApp')); beforeEach(inject(function ($rootScope, $controller) { $scope = $rootScope.$new(); $controller('MyController', { $scope: $scope }); })); it('should create "spices" model with 3 spices', function () { expect($scope.spices.length).toBe(3); }); it('should set the default value of spice', function () { expect($scope.spice).toBe('hello feenan!'); }); }); });
上面利用了$rootScope
來建立子做用域,而後把這個參數傳進控制器的構建方法$controller
裏去,最終會執行上面的控制器裏的方法,而後咱們檢查子做用域裏的數組數量以及字符串變量是否跟指望的值相等.
想要了解更多關於ng
裏的控制器的信息,能夠點擊這裏
定義一個簡單的指令
var app = angular.module('myApp', []); app.directive('aGreatEye', function () { return { restrict: 'E', replace: true, template: '<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>' }; });
而後咱們編寫一個簡單的測試腳本
describe('Unit testing great quotes', function () { var $compile; var $rootScope; // Load the myApp module, which contains the directive beforeEach(module('myApp')); // Store references to $rootScope and $compile // so they are available to all tests in this describe block beforeEach(inject(function (_$compile_, _$rootScope_) { // The injector unwraps the underscores (_) from around the parameter names when matching $compile = _$compile_; $rootScope = _$rootScope_; })); it('Replaces the element with the appropriate content', function () { // Compile a piece of HTML containing the directive var element = $compile("<a-great-eye></a-great-eye>")($rootScope); // fire all the watches, so the scope expression {{1 + 1}} will be evaluated $rootScope.$digest(); // Check that the compiled element contains the templated content expect(element.html()).toContain("lidless, wreathed in flame, 2 times"); }); });
上面的例子來自於官方提供的,最終上面的指令將會這用在html
裏使用
<a-great-eye></a-great-eye>
測試腳本里首先注入$compile
與$rootScope
兩個服務,一個用來編譯html
,一個用來建立做用域用,注意這裏的_
,默認ng
裏注入的服務先後加上_
時,最後會被ng
處理掉的,這兩個服務保存在內部的兩個變量裏,方便下面的測試用例能調用到
$compile
方法傳入原指令html
,而後在返回的函數裏傳入$rootScope
,這樣就完成了做用域與視圖的綁定,最後調用$rootScope.$digest
來觸發全部監聽,保證視圖裏的模型內容獲得更新
而後獲取當前指令對應元素的html
內容與指望值進行對比.
想要了解更多關於ng
裏的指令的信息,能夠點擊這裏
定義一個簡單的過濾器
var app = angular.module('myApp', []); app.filter('interpolate', ['version', function (version) { return function (text) { return String(text).replace(/\%VERSION\%/mg, version); }; }]);
而後編寫一個簡單的測試腳本
describe('filter', function () { beforeEach(module('myApp')); describe('interpolate', function () { beforeEach(module(function ($provide) { $provide.value('version', 'TEST_VER'); })); it('should replace VERSION', inject(function (interpolateFilter) { expect(interpolateFilter('before %VERSION% after')).toEqual('before TEST_VER after'); })); }); });
上面的代碼先配置過濾器模塊,而後定義一個version
值,由於interpolate
依賴這個服務,最後用inject
注入interpolate
過濾器,注意這裏的過濾器後面得加上Filter
後綴,最後傳入文本內容到過濾器函數裏執行,與指望值進行對比.
利用測試來開發NG有不少好處,能夠保證模塊的穩定性,還有一點就是可以深刻的瞭解ng的內部運行機制,因此建議用ng開發的同窗趕忙把測試補上吧!