• 基于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代码,代码可在appjsservices.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请求获得返回数据的长度。这个服务的测试代码如下,代码可在testunitservicesSpec.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();

  • 相关阅读:
    微信小程序的scheme码
    微信小程序的简单总结(uni-app)
    ES7-ES11新特性
    Promise 总结
    uni-app创建项目及使用 vant-weapp
    vscode 插件整理
    el-upload 组件总结
    从输入URL到页面显示过程中发生了什么
    实验 1:Mininet 源码安装和可视化拓扑工具
    2020软件工程第一次作业
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5343182.html
Copyright © 2020-2023  润新知