說說NG裏的單元測試

轉自:http://www.ngnice.com/posts/dc4b032b537ae0javascript

ng項目愈來愈大的時候,單元測試就要提上日程了,有的時候團隊是以測試先行,有的是先實現功能,後面再測試功能模塊,這個各有利弊,今天主要說說利用karmajasmine來進行ng模塊的單元測試.php


什麼是Karma

karma是一個單元測試的運行控制框架,提供以不一樣環境來運行單元測試,好比chrome,firfox,phantomjs等,測試框架支持jasmine,mocha,qunit,是一個以nodejs爲環境的npm模塊.html

安裝測試相關的npm模塊,通常的運行karma的話只須要下面兩個npm命令,第二個是一個測試框架,karma默認的。java

  • npm install -g karma
  • npm install -g karma-jasmine

   安裝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

jasmine是一個行爲驅動開發的測試框架,不依賴任何js框架以及dom,是一個很是乾淨以及友好API的測試庫.angularjs

下面簡單的以一個例子來講明它的用法github

定義一個測試文件命令爲test.jschrome

(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主要用來計算一個變量或者一個表達式的值,而後用來跟指望的值比較或者作一些其它的事件

  • beforeEachafterEach主要是用來在執行測試任務以前和以後作一些事情,上面的例子就是在執行以前改變變量的值,而後在執行完成以後重置變量的值

最後要說的是,describe函數裏的做用域跟普通JS同樣都是能夠在裏面的子函數裏訪問的

想要運行上面的測試例子能夠經過karar來運行,命令例子以下

karma start karma.conf.js

下面咱們重點的說說ng裏的控制器,指令,服務模塊的單元測試.

NG的單元測試

由於ng自己框架的緣由,模塊都是經過di來加載以及實例化的,因此爲了方便配合jasmine來編寫測試腳本,因此官方提供了angular-mock.js的一個測試工具類來提供模塊定義,加載,注入等.

下面說說ng-mock裏的一些經常使用方法

  • angular.mock.module 此方法一樣在window命名空間下,很是方便調用

module是用來配置inject方法注入的模塊信息,參數能夠是字符串,函數,對象,能夠像下面這樣使用

beforeEach(module('myApp.filters')); beforeEach(module(function($provide) { $provide.value('version', 'TEST_VER'); })); 

它通常用在beforeEach方法裏,由於這個能夠確保在執行測試任務的時候,inject方法能夠獲取到模塊配置

  • angular.mock.inject 此方法一樣在window命名空間下,很是方便調用

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開發的同窗趕忙把測試補上吧!

相關文章
相關標籤/搜索