1. 程式人生 > >AngularJS實際專案應用——單元測試框架設計

AngularJS實際專案應用——單元測試框架設計

轉自:http://www.cnblogs.com/vipyoumay/p/5331787.html

關於本文:介紹通過karma與jsmine框架對angular開發的應用程式進行單元與E2E測試。

先決條件

  • nodejs
  • webstorm

建立專案

webstorm中建立空白web專案

建立html、js資料夾

在專案中建立2個資料夾分別用於存放專案中用到的html、js檔案。

安裝框架

安裝前端框架

專案中的前端框架主要為angularjs相關的框架,為了安裝框架方便可安裝bower包管理器。

1) 安裝bower包管理器

在webstorm的terminal中執行指令碼

npm install bower -save

2) 初始化bower.json檔案

執行指令碼生成bower.json檔案,用於管理bower的依賴和配置。

bower init

3) 安裝angular等框架

除了專案要用到的angular框架外還需要安裝angular-mocks框架

bower install bootstrap -save
bower install angular -save
bower install angular-mocks -save

安裝伺服器端框架

伺服器依賴於nodejs,需要安裝nodejs的包,首先在根目錄下建立package.json檔案。

1)安裝http-server模組

npm install http-server -save

2)安裝其他模組

  • jasmine-core:javascript單元測試框架;
  • karma:模擬javascript指令碼在各種瀏覽器執行的工具;
  • karma-chrome-launcher: 在chrome瀏覽器執行的工具;
  • karma-jasmine: jasmine-core在karma中的介面卡;
  • karma-junit-reporter: 生成junit報告;
  • protractor:E2E測試框架

啟動伺服器

要啟動node伺服器需要在package.json中配置script節點,dependencies中定義依賴包,在script配置start節點用於啟動伺服器,test節點的內容會在後面講解。

"name": "angularjs-test",
  "version": "0.0.1",
  "dependencies": {
    "bower": "^1.7.7",
    "http-server": "^0.9.0",
    "jasmine-core": "^2.4.1",
    "karma": "^0.13.22",
    "karma-chrome-launcher": "^0.2.3",
    "karma-jasmine": "^0.3.8",
    "karma-junit-reporter": "^0.4.1",
    "protractor": "^3.2.1"
  },
  "scripts": {
    "postinstall": "bower install",
    "prestart": "npm install",
    "start": "http-server -a localhost -p 8000 -c-1",
    "pretest": "npm install",
    "test": "karma start karma.conf.js",
    "test-single-run": "karma start karma.conf.js  --single-run"
  }

配置後執行命令,啟動伺服器,瀏覽器上輸入http://localhost:8000

npm start

開始單元測試

編寫功能程式碼

在檔案js中新建js檔案index.js。在index.js中定義congroller,實現簡單累加方法add,程式碼如下:

/** * Created by stephen on 2016/3/24. */

(function (angular) {
    angular.module('app', []).
    controller('indexCtrl', function ($scope) {
        $scope.add = function (a, b) {
            if(a&&b)
            return Number(a) + Number(b)
            return 0;
        }
    });
})(window.angular);

在檔案html中新建html檔案index.html,加入兩個輸入框使用者獲取輸入,當輸入後繫結controller中的add方法實現計算器功能,程式碼如下:

<!DOCTYPE html>
<html lang="en" ng-app="app"><head><meta charset="UTF-8"><title>index</title></head><body><div ng-controller="indexCtrl"><input type="text" ng-model="a" value="0">+<input type="text" ng-model="b" value="0">=<span id='result'>{{add(a,b)}}</span></div></body></html><script src="/bower_components/angular/angular.min.js"></script><script src="/bower_components/angular-mocks/angular-mocks.js"></script><script src="/js/index.js"></script>

啟動伺服器看到下圖效果

編寫測試程式碼

在test資料夾中新建檔案index-test.js用於編寫index.js的單元測試。

'use strict';
describe('app', function () {
    beforeEach(module('app'));
    describe('indexCtrl', function () {
        it('add 測試', inject(function ($controller) {
            var $scope = {};
            //spec body
            var indexCtrl = $controller('indexCtrl', {$scope: $scope});
            expect(indexCtrl).toBeDefined();
            expect($scope.add(2, 3)).toEqual(5);
        }));

    });
});

單元測試配置

初始化karma配置檔案,用於配置karma,執行命令

karma init

在karma配置檔案程式碼中每個節點都有預設註釋請參看

module.exports = function (config) {
    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: './',


        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],


        // list of files / patterns to load in the browser
        files: [
            'bower_components/angular/angular.min.js',
            'bower_components/angular-mocks/angular-mocks.js',
            'js/index.js',
            'test/index-test.js'
        ],

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress'],


        // web server port
        port: 9876,


        // enable / disable colors in the output (reporters and logs)
        colors: true,


        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,


        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,


        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome'],


        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false,

        plugins: [
            'karma-chrome-launcher',
            'karma-jasmine',
            'karma-junit-reporter'
        ],

        junitReporter: {
            outputFile: '/test_out/unit.xml',
            suite: 'unit'
        }
    })
}

在package.json scripts 配置測試資訊,指定karma檔案地址

"test": "karmastartkarma.conf.js",

執行單元測試

執行命令,執行測試

npm test

執行結果如下,可以看到通過測試:

除錯單元測試

除了執行測試外,很多時候需要除錯測試,在karma彈出網頁中點選debug,進入http://localhost:9876/debug.html頁面,就可以用chrome自帶的除錯工具除錯程式碼了:

單元測試覆蓋率

如果需要對單元測試覆蓋率進行統計,可以安裝karma-coverage並配置karma檔案。這樣在單元測試完成後,會生成測試覆蓋率報告文件。

npm install karma-coverage -save

在karma.conf.js檔案中加入節點


// 新增節點用於配置輸出資料夾
coverageReporter: {
           type: 'html',
           dir: 'coverage'
       },
// 新增節點用於配置需要測試的檔案地址(這裡是controller地址)
preprocessors: {'js/*.js': ['coverage']}

// 新增元素'karma-coverage'
plugins: [
          'karma-chrome-launcher',
          'karma-jasmine',
          'karma-junit-reporter',
          'karma-coverage',
      ],
// 新增元素 coverage
reporters: ['progress', 'coverage'],

執行單元測試後在目錄中生成coverage資料夾,點選index.html可以檢視測試覆蓋率。

E2E測試

e2e或者端到端(end-to-end)或者UI測試是一種測試方法,它用來測試一個應用從頭到尾的流程是否和設計時候所想的一樣。簡而言之,它從一個使用者的角度出發,認為整個系統都是一個黑箱,只有UI會暴露給使用者。

配置E2E測試

新建資料夾e2e-test新建protractor.conf.js檔案,用於配置protractor框架,程式碼如下。

exports.config = {

    allScriptsTimeout: 11000,

    baseUrl: 'http://localhost:8000/app/',

    // Capabilities to be passed to the webdriver instance.
    capabilities: {
        'browserName': 'chrome'
    },

    framework: 'jasmine',

    // Spec patterns are relative to the configuration file location passed
    // to protractor (in this example conf.js).
    // They may include glob patterns.
    specs: ['*.js'],

    // Options to be passed to Jasmine-node.
    jasmineNodeOpts: {
        showColors: true, // Use colors in the command line report.
    },

    defaultTimeoutInterval: 30000
};

配置package.json scripts指令碼節點

"preupdate-webdriver": "npminstall",
"update-webdriver": "webdriver-managerupdate",
"preprotractor": "npmrunupdate-webdriver",
"protractor": "protractore2e-test/protractor.conf.js"

編寫e2e測試指令碼

設計測試用例:文字框a的值錄入1,文字框b錄入2,期望結果3

describe('index.html', function() {

    beforeEach(function() {
        browser.get('http://localhost:8000/html');
    });

    it('get index html', function() {

        var a = element(by.model('a'));
        var b = element(by.model('b'));
        a.sendKeys(1);
        b.sendKeys(2);
        var result = element(by.id('result'));
        expect(result.getText()).toEqual('3');
    });
});

執行測試檢視測試結果

需要執行命名,檢視是否更新webdriver(什麼是webdriver http://sentsin.com/web/658.html),
手動安裝protractor至全域性
npm i -g protractor

注:安裝或更新webdriver需要FQ,請在webstrom中設定代理地址
webstrom中切換至Terminal視窗,在Terminal視窗通過以下方式設定代理:

set PROXY=http://localhost:1080
set HTTP_PROXY=%PROXY%
set HTTPS_PROXY=%PROXY%

代理設定成功後,執行以下命令

npm run update-webdriver

執行e2e測試,這是會彈出瀏覽器,自動點選瀏覽器,錄入指令碼輸入完成自動化e2e測試,其本質還是呼叫selenium測試。

npm run protractor

參考資料

================================================================= 這裡我要說幾點: 1)controller中用了ngload類似的非同步載入機制,去載入檔案,在用karma跑的時候有時候會出錯,因為不能保證ngload已經把檔案載入進來了。我們看上面的測試case都是用$controller方法去例項化controller的,這樣就會出我說的問題。所以需要改用$injector.instantiate這個方法去例項化,用instantiate這個方法,對controller的寫法有要求,可以參考我介紹controller的文章,怎麼寫controller,下面看一個例子:
var ldapCtrlInst = $injector.instantiate(LDAPCtrlDef,{
  					$scope:scope,
  					ldapSrv:ldapSrv,
  					uiGridUtils:{
  						createGridOptions:function(){return {};},
  						resetGridSelection:jasmine.createSpy('resetGridSelection')
  					},
  					modalUtils:{
  						showDlg:function(){
  							var deferred = $q.defer();
  							deferred.resolve("");
  							return deferred.promise;
  						}
  					}
  				});
上面程式碼中的LDAPCtrlDef是通過define把ldap.controller.js引入進來時的變數,然後$scope,ldapSrv,uiGridUtils等都是需要注入controller的引數。 2)由於是單元測試,不需要真正呼叫後臺的service,可以用下面的方式進行模擬
var deferred = $q.defer();
deferred.resolve({resultCode:"200",resultObject:[]});
spyOn(ldapSrv,'listLDAP').and.returnValue(deferred.promise);

3)在it方法裡記得呼叫scope.$digest();觸發angular的變數改變繫結機制