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 相关文章推荐
深入聊聊Array的sort方法的使用技巧.详细点评protype.js中的sortBy方法
Apr 12 Javascript
鼠标事件延时切换插件
Mar 12 Javascript
DIV+CSS+JS不间断横向滚动实现代码
Mar 19 Javascript
javascript模拟命名空间
Apr 17 Javascript
JavaScript实现带标题的图片轮播特效
May 20 Javascript
JavaScript中绑定事件的三种方式及去除绑定
Nov 05 Javascript
js仿百度音乐全选操作
Jan 13 Javascript
node.js+jQuery实现用户登录注册AJAX交互
Apr 28 jQuery
jQuery EasyUI 折叠面板accordion的使用实例(分享)
Dec 25 jQuery
js 图片转base64的方式(两种)
Apr 24 Javascript
详解easyui 切换主题皮肤
Apr 04 Javascript
JavaScript 实现轮播图特效的示例
Nov 05 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
如何开发一个虚拟域名系统
2006/10/09 PHP
PHP常用代码
2006/11/23 PHP
PHP根据两点间的经纬度计算距离
2014/10/31 PHP
PHP+Redis 消息队列 实现高并发下注册人数统计的实例
2018/01/29 PHP
laravel 实现用户登录注销并限制功能
2019/10/24 PHP
同一个表单 根据要求递交到不同页面的实现方法小结
2009/08/05 Javascript
用JQuery实现表格隔行变色和突出显示当前行的代码
2012/02/10 Javascript
javascript实现图片上传前台页面
2015/08/18 Javascript
jQuery+html5+css3实现圆角无刷新表单带输入验证功能代码
2015/08/21 Javascript
探索webpack模块及webpack3新特性
2017/09/18 Javascript
基于vue-resource jsonp跨域问题的解决方法
2018/02/03 Javascript
vue2.0结合Element-ui实战案例
2019/03/06 Javascript
微信小程序(订阅消息)功能
2019/10/25 Javascript
微信小程序获取复选框全选反选选中的值(实例代码)
2019/12/17 Javascript
Vue结合路由配置递归实现菜单栏功能
2020/06/16 Javascript
微信小程序通过websocket实时语音识别的实现代码
2020/08/19 Javascript
Python下的Mysql模块MySQLdb安装详解
2014/04/09 Python
python处理大数字的方法
2015/05/27 Python
Python爬虫天气预报实例详解(小白入门)
2018/01/24 Python
Python实现的读取电脑硬件信息功能示例
2018/05/30 Python
Python闭包执行时值的传递方式实例分析
2018/06/04 Python
Python字符串的一些操作方法总结
2019/06/10 Python
django 中的聚合函数,分组函数,F 查询,Q查询
2019/07/25 Python
安装PyInstaller失败问题解决
2019/12/14 Python
美国办公用品购物网站:Quill.com
2016/09/01 全球购物
GOOD AMERICAN官网:为曲线性感而设计
2017/12/28 全球购物
会计工作心得体会
2014/01/13 职场文书
试用期转正鉴定评语
2014/01/27 职场文书
财务信息服务专业自荐书范文
2014/02/08 职场文书
共青团员自我评价范文
2014/09/14 职场文书
学校组织向国旗敬礼活动方案(中小学适用)
2014/09/27 职场文书
婚前协议书范本
2014/10/27 职场文书
土建技术员岗位职责
2015/04/11 职场文书
教师节联欢会主持词
2015/07/04 职场文书
详解MySQL 联合查询优化机制
2021/05/10 MySQL
如何Python使用re模块实现okenizer
2022/04/30 Python