angularjs中的单元测试实例


Posted in Javascript onDecember 06, 2014

当ng项目越来越大的时候,单元测试就要提上日程了,有的时候团队是以测试先行,有的是先实现功能,后面再测试功能模块,这个各有利弊,今天主要说说利用karma和jasmine来进行ng模块的单元测试.

什么是Karma

karma是一个单元测试的运行控制框架,提供以不同环境来运行单元测试,比如chrome,firfox,phantomjs等,测试框架支持jasmine,mocha,qunit,是一个以nodejs为环境的npm模块.

安装测试相关的npm模块建议使用----save-dev参数,因为这是开发相关的,一般的运行karma的话只需要下面两个npm命令

npm install karma --save-dev

npm install karma-junit-reporter --save-dev

安装karma的时候会自动的安装一些常用的模块,参考karma代码里的package.json文件的peerDependencies属性

 "peerDependencies": {

        "karma-jasmine": "~0.1.0",

        "karma-requirejs": "~0.2.0",

        "karma-coffee-preprocessor": "~0.1.0",

        "karma-html2js-preprocessor": "~0.1.0",

        "karma-chrome-launcher": "~0.1.0",

        "karma-firefox-launcher": "~0.1.0",

        "karma-phantomjs-launcher": "~0.1.0",

        "karma-script-launcher": "~0.1.0"

  }

然后一个典型的运行框架通常都需要一个配置文件,在karma里可以是一个karma.conf.js,里面的代码是一个nodejs风格的,一个普通的例子如下:

module.exports = function(config){

  config.set({

    // 下面files里的基础目录

    basePath : '../',

    // 测试环境需要加载的JS信息

    files : [

      'app/bower_components/angular/angular.js',

      'app/bower_components/angular-route/angular-route.js',

      'app/bower_components/angular-mocks/angular-mocks.js',

      'app/js/**/*.js',

      'test/unit/**/*.js'

    ],

    // 是否自动监听上面文件的改变自动运行测试

    autoWatch : true,

    // 应用的测试框架

    frameworks: ['jasmine'],

    // 用什么环境测试代码,这里是chrome`

    browsers : ['Chrome'],

    // 用到的插件,比如chrome浏览器与jasmine插件

    plugins : [

            'karma-chrome-launcher',

            'karma-firefox-launcher',

            'karma-jasmine',

            'karma-junit-reporter'

            ],

    // 测试内容的输出以及导出用的模块名

    reporters: ['progress', 'junit'],

    // 设置输出测试内容文件的信息

    junitReporter : {

      outputFile: 'test_out/unit.xml',

      suite: 'unit'

    }
  });

};

这里要注意的时,上面的插件大部分都不需要单独安装,因为安装karma的时候已经安装了,这里只有karma-junit-reporter导出插件需要单独安装,想要了解更多的关于配置文件的信息可以,点击这里

karma就讲到这里,想了解更多关于它的信息可以,点击这里

什么是jasmine

Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.

上面是jasmine官方文档里对它的解释,下面用中文简单的翻译下

jasmine是一个行为驱动开发的测试框架,不依赖任何js框架以及dom,是一个非常干净以及友好API的测试库.

下面简单的以一个例子来说明它的用法

定义一个测试文件命令为test.js

describe("A spec (with setup and tear-down)", function() {

  var foo;
  beforeEach(function() {

    foo = 0;

    foo += 1;

  });
  afterEach(function() {

    foo = 0;

  });
  it("is just a function, so it can contain any code", function() {

    expect(foo).toEqual(1);

  });
  it("can have more than one expectation", function() {

    expect(foo).toEqual(1);

    expect(true).toEqual(true);

  });

});

上面的例子来自于官网,这里只说下几个重要的API,更多的用法请,点击这里

1.首先任何一个测试用例以describe函数来定义,它有两参数,第一个用来描述测试大体的中心内容,第二个参数是一个函数,里面写一些真实的测试代码

2.it是用来定义单个具体测试任务,也有两个参数,第一个用来描述测试内容,第二个参数是一个函数,里面存放一些测试方法

3.expect主要用来计算一个变量或者一个表达式的值,然后用来跟期望的值比较或者做一些其它的事件

4.beforeEach与afterEach主要是用来在执行测试任务之前和之后做一些事情,上面的例子就是在执行之前改变变量的值,然后在执行完成之后重置变量的值

最后要说的是,describe函数里的作用域跟普通JS一样都是可以在里面的子函数里访问的,就像上面的it访问foo变量

想要运行上面的测试例子可以通过karar来运行,命令例子如下:

karma start test/karma.conf.js

下面我们重点的说说ng里的控制器,指令,服务模块的单元测试.

NG的单元测试

因为ng本身框架的原因,模块都是通过di来加载以及实例化的,所以为了方便配合jasmine来编写测试脚本,所以官方提供了angular-mock.js的一个测试工具类来提供模块定义,加载,注入等.

下面说说ng-mock里的一些常用方法

1.angular.mock.module 此方法同样在window命名空间下,非常方便调用

module是用来配置inject方法注入的模块信息,参数可以是字符串,函数,对象,可以像下面这样使用

beforeEach(module('myApp.filters'));
beforeEach(module(function($provide) {

      $provide.value('version', 'TEST_VER');

}));

它一般用在beforeEach方法里,因为这个可以确保在执行测试任务的时候,inject方法可以获取到模块配置

1.angular.mock.inject 此方法同样在window命名空间下,非常方便调用

inject是用来注入上面配置好的ng模块,方面在it的测试函数里调用,常见的调用例子如下:

angular.module('myApplicationModule', [])

      .value('mode', 'app')

      .value('version', 'v1.0.1');


  describe('MyApp', function() {
    // You need to load modules that you want to test,

    // it loads only the "ng" module by default.

    beforeEach(module('myApplicationModule'));


    // inject() is used to inject arguments of all given functions

    it('should provide a version', inject(function(mode, version) {

      expect(version).toEqual('v1.0.1');

      expect(mode).toEqual('app');

    }));


    // The inject and module method can also be used inside of the it or beforeEach

    it('should override a version and test the new version is injected', function() {

      // module() takes functions or strings (module aliases)

      module(function($provide) {

        $provide.value('version', 'overridden'); // override version here

      });
      inject(function(version) {

        expect(version).toEqual('overridden');

      });

    });

  });

上面是官方提供的一些inject例子,代码很好看懂,其实inject里面就是利用angular.inject方法创建的一个内置的依赖注入实例,然后里面的模块注入跟普通ng模块里的依赖处理是一样的

简单的介绍完ng-mock之后,下面我们分别以控制器,指令,过滤器来编写一个简单的单元测试.

ng里控制器的单元测试

定义一个简单的控制器

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里的控制器的信息,可以点击这里

ng里指令的单元测试

定义一个简单的指令

var app = angular.module('myApp', []);
app.directive('aGreatEye', function () {

    return {

        restrict: 'E',

        replace: true,

        template: '<h1>lidless, wreathed in flame, 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 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里的指令的信息,可以点击这里

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开发的同学赶紧把测试补上吧!

Javascript 相关文章推荐
IE6与IE7中,innerHTML获取param的区别
Mar 15 Javascript
浅析Javascript使用include/require
Nov 13 Javascript
PHP使用方法重载实现动态创建属性的get和set方法
Nov 17 Javascript
jQuery+CSS实现简单切换菜单示例
Jul 27 Javascript
JavaScript制作弹出层效果
Dec 02 Javascript
Bootstrap布局之栅格系统学习笔记
May 04 Javascript
AngularJS页面带参跳转及参数解析操作示例
Jun 28 Javascript
对vue.js中this.$emit的深入理解
Feb 23 Javascript
layui表单提交到后台自动封装到实体类的方法
Sep 12 Javascript
echarts实现折线图的拖拽效果
Dec 19 Javascript
详解jQuery中的prop()使用方法
Jan 05 jQuery
Java无向树分析 实现最小高度树
Apr 09 Javascript
angularjs指令中的compile与link函数详解
Dec 06 #Javascript
angularjs的一些优化小技巧
Dec 06 #Javascript
JavaScript开发人员的10个关键习惯小结
Dec 05 #Javascript
node.js中RPC(远程过程调用)的实现原理介绍
Dec 05 #Javascript
node.js中实现同步操作的3种实现方法
Dec 05 #Javascript
node.js实现BigPipe详解
Dec 05 #Javascript
js实现点击添加一个input节点
Dec 05 #Javascript
You might like
PHP与javascript对多项选择的处理
2006/10/09 PHP
PHP中Date获取时间不正确怎么办
2008/06/05 PHP
无需重新编译php加入ftp扩展的解决方法
2013/02/07 PHP
浅析51个PHP处理字符串的函数
2013/08/02 PHP
php随机显示指定文件夹下图片的方法
2015/07/13 PHP
Iframe thickbox2.0使用的方法
2009/03/05 Javascript
jquery弹出框的用法示例(2)
2013/08/26 Javascript
jquery单行文字向上滚动效果的实现代码
2014/09/05 Javascript
值得分享的bootstrap table实例
2016/09/22 Javascript
js利用appendChild对标签进行排序的实现方法
2016/10/16 Javascript
纯JavaScript手写图片轮播代码
2016/10/20 Javascript
微信小程序 获取相册照片实例详解
2016/11/16 Javascript
工作中常用的js、jquery自定义扩展函数代码片段汇总
2016/12/22 Javascript
JavaScript 基础表单验证示例(纯Js实现)
2017/07/20 Javascript
javaScript产生随机数的用法小结
2018/04/21 Javascript
深入理解js 中async 函数的含义和用法
2018/05/13 Javascript
JavaScript高级函数应用之分时函数实例分析
2018/08/03 Javascript
使用ECharts实现状态区间图
2018/10/25 Javascript
JS 封装父页面子页面交互接口的实例代码
2019/06/25 Javascript
js 动态校验开始结束时间的实现代码
2020/05/25 Javascript
js实现鼠标点击飘爱心效果
2020/08/19 Javascript
详解如何在Javascript中使用Object.freeze()
2020/10/18 Javascript
Python实现的单向循环链表功能示例
2017/11/10 Python
Django框架模板注入操作示例【变量传递到模板】
2018/12/19 Python
情人节快乐! python绘制漂亮玫瑰
2020/08/18 Python
Python any()函数的使用方法
2019/10/28 Python
Python实现京东抢秒杀功能
2021/01/25 Python
餐饮管理自我介绍信
2014/01/15 职场文书
三年大学生活自我鉴定
2014/01/21 职场文书
闭幕式主持词
2014/04/02 职场文书
无偿献血倡议书
2014/04/14 职场文书
节能标语大全
2014/06/21 职场文书
雷锋式好少年事迹材料
2014/08/17 职场文书
2015个人年度工作总结范文
2015/05/28 职场文书
加薪申请书应该这样写!
2019/07/04 职场文书
python使用shell脚本创建kafka连接器
2022/04/29 Python