基於karma和jasmine的Angularjs 單元測試


Angularjs 基於karma和jasmine的單元測試

目錄:

1. 單元測試的配置

2. 實例文件目錄解釋

3. 測試controller

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

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

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

5. 測試返回promise的service

 

已經有很多教程提到了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();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM