Angularjs 基於karma和jasmine的單元測試

目錄:jquery

1. 單元測試的配置git

2. 實例文件目錄解釋angularjs

3. 測試controllergithub

    3.1 測試controller中變量值是否正確chrome

    3.2 模擬http請求返回值,測試$http服務相關npm

4. 從文件中讀取json,來模擬 http請求返回數據json

5. 測試返回promise的servicepromise

 

已經有不少教程提到了angularjs項目的單元測試,但大都不是很全,如一些入門的文章,介紹了測試http service 卻沒有介紹如何從文件中讀取測試數據來仿真。一些介紹如何從文件中讀取仿真數據的文章對入門則太深刻。因此寫了這個在工做中常常會遇到的狀況的教程。但願有點用:)瀏覽器

 

1. 單元測試的配置

  1. 安裝 angular
        npm install angular --save
  2. 安裝 karma
      npm install -g karma --save-dev
  3. 安裝 Jasmine
        npm install karma-jasmine jasmine-core --save-dev
  4. 安裝 ngMock
        npm install angular-mocks --save-dev
  5. 安裝 jasmine-jquery
        bower install jasmine-jquery --save
  6. 安裝 karma-read-json
        bower install karma-read-json
  7. 下載實例 
    https://github.com/wuhaibo/angularUnitTest

2. 實例文件目錄解釋  

 

3. 測試controller

首先看看咱們的controller的代碼網絡

 1  'use strict' 2 /* Controllers */
 3 /* module */
 4 var unitTestApp = angular.module('unitTestApp', []);
 5  
 6 /* Controllers */
 7 unitTestApp.controller('unitTestCtrl', function($scope,$http) {
 8   
 9     //set name 
10     $scope.name = "william wood";
11     
12     //經過http請求獲得user
13     $scope.GetUser = function(){
14         $http.get('/auth.py').then(function(response) {
15         $scope.user = response.data;
16     });
17   };
18 });

這個controller很簡單, 有兩個元素

  1. scope裏聲明瞭一個變量name, 並賦值 williamwood
  2. 定義了一個函數GetUser 這個函數發送一個http get請求,來給scope.user 賦值

 

咱們先測試 1 再測試 2.

 

3.1 測試controller中變量值是否正確

測試的代碼在  /test/unit/controllersSpec.js, 測試代碼簡單說明以下

 1 'use strict';
 2  
 3 //測試類型描述,這裏表示測試unitTestApp的controllers
 4 describe('unitTestApp controllers', function() {
 5  
 6   //測試類型描述,這裏表示測試unitTestCtrl這個controller
 7   describe('unitTestCtrl', function(){
 8       
 9     //beforeEach 表示在運行全部測試前的準備工做。
10     //這裏生成unitTestApp 的module
11     beforeEach(module('unitTestApp'));
12     
13     //定義在測試中會用到的object,以便在整個測試環境中使用
14     var scope,ctrl;
15 
16         //inject利用angular的依賴注入,將須要的模塊,服務插入做用域
17     beforeEach(inject(function ($controller, $rootScope) {
18         //模擬生成scope, $rootScope是angular中的頂級scope,angular中每一個controller中的     
19         //scope都是rootScope new出來的
20         scope = $rootScope.$new();
21         //模擬生成controller 並把先前生成的scope傳入以方便測試
22         ctrl = $controller('unitTestCtrl', {$scope: scope});
23     }));
24         
25     //測試從這裏開始
26     // it 裏'should create name william wood in unitTestCtrl' 說明測試的項目
27     it('should create name william wood in unitTestCtrl', 
28        inject(function() {
29         //測試指望 scope.name 的值爲 william wood  
30         expect(scope.name).toEqual('william wood');
31     }));
32  
33     //測試GetUser函數,詳細將在下面介紹 
34     it('GetUser should fetch users', inject(function($injector){
35            ....        
36     }));
37   });
38 });
39  

jasmine中用describe來描述testcase類別(如是測試哪一個controller,哪一個modular。。。), beforeEach 用來作測試前的準備工做,inject利用angular的依賴注入,將須要的模塊,服務插入做用域。真正的測試代碼在it函數裏,這個函數的第一個參數爲testcase描述,第二個函數爲測試邏輯.

測試配置(能夠跳過這一步)

測試能夠用karma init命令配置, 這個命令會生成karma.conf.js 文件來做爲測試配置文件。因爲實例文件夾中已經有了這個文件就能夠跳過這一步。之後可使用實例文件結構做爲其餘項目的基礎模板。

運行測試

 

1. Windows commandline 進入到 karma.conf.js 所在目錄。

2. 運行指令 karma start, 這時會彈出瀏覽器窗口,不用管,它們被啓動來執行測試,就讓他們在後臺呆着就能夠。 karma會自動監視文件改動自動執行測試。測試成功以下圖所示,這裏由於在測試文件中有兩個測試用例,因此能夠看到 Executed 1 of 2 … 字樣爲了測試方便,firefox測試平臺被註釋掉,全部測試將只在chrome上運行,若是要使用firefox來運行測試只須要將karma.conf.js 裏的 browsers : ['Chrome'/*, 'Firefox'*/] 改成 browsers : ['Chrome', 'Firefox']便可)

 

3. 測試失敗的狀況
修改expect(scope.name).toEqual('william wood')
expect(scope.name).toEqual('william wood is me'); 
保存後切換到命令行窗口,發現測試自動運行了,並有錯誤報告。

 

Ok 到此爲止咱們已經能夠測試一個controller了。下面咱們介紹如何模擬http請求的返回值測試$http服務相關的邏輯。

 

3.2 模擬http請求返回值,測試$http服務相關

記得咱們在controller中有一個GetUser函數

1 //經過http請求獲得user
2     $scope.GetUser = function(){
3         $http.get('/auth.py').then(function(response) {
4         $scope.user = response.data;
5     });

這個函數經過http get請求獲得user的值。

在單元測試裏咱們並不真的但願發送一個http get請求來運行測試,由於那樣會使測試複雜化,網絡相關的各類問題都會致使測試失敗,並且angular http服務是異步的,而咱們但願測試是同步的。那麼怎麼作呢?

先來看測試的代碼,仍然在 /test/unit/controllersSpec.js

     //模擬http get的返回值, 插入injector服務,讓咱們可以在測試代碼中使用依賴注入來得到須要的服務
    it('GetUser should fetch users', inject(function($injector){
        // $httpBackend 是由angular mock提供的一個模擬http請求返回服務
        // 能夠用它來模擬http請求的返回值
        // 這裏經過$injector來獲取它的實例        
        var $httpBackend = $injector.get('$httpBackend');
        
        // $httpBackend 在Get方法,對 '/auth.py' 的url將會返回 一個jason對象
        // {customerId: '1',name:'benwei'} 
        $httpBackend.when('GET', '/auth.py').respond({customerId: '1',name:'benwei'});
        
        //以上爲測試前的準備工做, 也能夠把這部分代碼放在beforeEach裏,
        //但要注意: beforeEach裏的設置將影響全部在它做用域的測試用例。
        
        
        //運行GetUser函數        
        scope.GetUser();
        
        //把http的異步轉爲同步,要求$httpBackend馬上返回數據 
        $httpBackend.flush();
        
        // 查看scope.user的值是否正確
        expect(scope.user).toEqual({customerId: '1',name:'benwei'});
    }));
 

4. 從文件中讀取json,來模擬 http請求返回數據

有些時候咱們須要返回比較大的json數據, 這時json數據像上面這樣寫在測試代碼裏就不大現實。比較可行的方案是把json數據保存在json文件中,並從文件中讀取數據。這時咱們就須要Karma-Read-JSON的幫助。

 

咱們已經在單元測試的配置中安裝了這個插件,並在 /test/karma.conf.js 中作了設置,這裏對設置進行簡單的說明。(能夠跳過閱讀這一步,只要記得將模擬使用的 json文件放在 test/mock/ 文件夾中,而且文件後綴爲.json)

1.在測試中引入karma-read-json框架

   files : [
        //test framework
       'app/bower_components/karma-read-json/karma-read-json.js',
      ],

2. 向karma指定在測試中會用到的模擬數據文件格式,

    files : [
           …   
           // fixtures
           {pattern: 'test/mock/*.json',  included: false},
           …
    ],

注意這裏的根目錄是在karma.conf.js文件中設置的,以下

  basePath : '../', //設置 karma.conf.js所在目錄/../  爲根目錄

在本實例中模擬數據須要的 json文件應該放在test/mock 文件夾中

當設置進行完後,再來看咱們的測試代碼

      it('GetUser should fetch users mock response from file', 
        inject(function($injector){
            
        //從文件中讀取模擬返回數據    
        var valid_respond = readJSON('test/mock/data.json');        
 
        // 這裏經過$injector來獲取它的實例獲取 httpBackend服務的實例    
        var $httpBackend = $injector.get('$httpBackend');
        
        // $httpBackend 在Get方法,對 '/auth.py' 的url將會返回 
        // 一個從test/mock/data.json讀取的json對象
        $httpBackend.when('GET', '/auth.py').respond(valid_respond);
               
         // $httpBackend 在Get方法,對 '/auth.py' 的url將會返回 一個jason對象
        // {customerId: '1',name:'benwei'} 
        $httpBackend.when('GET', '/auth.py').respond({customerId: '1',name:'benwei'});
        
        //運行GetUser函數        
        scope.GetUser();
        
        //把http的異步轉爲同步,要求$httpBackend馬上返回數據 
        $httpBackend.flush();
        
        // 查看scope.user的值是否正確
        expect(scope.user.length).toBe(2);       
       }));

5. 測試返回promiseservice

先來看看service代碼,代碼可在app\js\services.js 中找到

'use strict';
 
/* Services */
unitTestApp.factory('GetUserNumberService',    
    function($http,$q) {
       var deferred = $q.defer();
       
       //http 服務請求
       $http({method: 'GET', url: '/auth.py'}).then(
           function(response){
              deferred.resolve(response.data.length); 
           },
           function (response) {
              deferred.reject(response); 
           }
       );
       
       //返回http 服務請求的promise
       return deferred.promise;  
    }
);
 

咱們建立了一個叫 GetUserNumberService 的服務,這個服務經過發送http請求得到返回數據的長度。這個服務的測試代碼以下,代碼可在 test\unit\servicesSpec.js 中找到

'use strict';
 
/* jasmine specs for services go here */
  
describe('serviceTest', function() {
    
describe('Test GetUserNumberService', function() {
 
    //mock module 
    beforeEach(module('unitTestApp'));
    
    it('GetUserNumberService should return 2', 
        inject(function($injector) {
 
       //模擬返回數據   
        var valid_respond = '[{"customerId": "1","name": "benwei"},{"customerId": "2","name": "william"}]';
        var $httpBackend = $injector.get('$httpBackend');
        $httpBackend.whenGET('/auth.py').respond(valid_respond);
         
         // 經過injector獲得service,就像在前面的例子中獲得$httpBackend同樣     
         var getUserNumberService = $injector.get('GetUserNumberService');          
         var promise = getUserNumberService;
         var userNum;
         promise.then(function(data){
             userNum = data;
         });
         
         //強迫httpBackend返回數據
         $httpBackend.flush();
        
        //經過injector獲得$rootScope      
        var $rootScope = $injector.get('$rootScope');         
         //強迫傳遞到當前做用域
        $rootScope.$apply();     
         
        //測試判斷userNum是否爲2
        expect(userNum).toEqual(2);     
    }));
 
});
});
 

有一個值得注意的地方, 爲了將變化傳遞到當前做用域,因此要使用 $rootScope.$apply();

相關文章
相關標籤/搜索