利用 Grunt (几乎)无痛地做前端开发 (一)之单元测试
2016-06-08 16:38
316 查看
前言
如果你想开发一个js应用,甭管多简单,都要先创建整个宇宙
来看看我们的Javascript小宇宙:
确定如何根据需求、功能划分模块,如何将代码分成多个文件开发,合成一个发布
保证上一条的同时,使用 Coffeescript、SCSS/LESS 等技术
保证上2条的同时,想想怎么在浏览器的刷新后一切都最新
保证上3条的同时,想想怎么在开发、测试、生产、预发布环境中都OK
保证上4条的同时,进行TDD式的开发
…这还是js, 我们还没有涉及到HTML
Grunt 可以将创建小宇宙的工作变得轻松很多。
初识 grunt
以一个jQuery插件的开发为例。 开始之前,让我们先安装Grunt.首先需要Nodejs环境,这里假设你已经安装好了Nodejs和NPM.
然后安装 grunt :
npm install grunt
命令行方式更适合前端开发。这里我都用命令行来进行操作,windows用户推荐用git-shell或者powershell.
第一招:快速搭建脚手架
Grunt 已经安装好了,第一步就是利用grunt快速搭建脚手架出来。所谓的脚手架,就是指包含了目录结构和初始的一些功能,测试文件的一个环境。我们来搭建一个jquery插件的脚手架:grunt init:jquery
这时grunt会问你一些问题,比如项目名称, git地址,作者等等,比如:
![](http://img03.taobaocdn.com/tfscom/T1EwSRXyNhXXaCwpjX.png_620x10000.jpg)
可以看到,grunt已经帮我们创建了一些经常出现的文件(README, LICENSE等), 单元测试也准备好了。
第二招:TDD(测试驱动开发) 和 单元测试
现在我们就可以着手写grunt-demo-1这个插件了。假设我们这个插件的目的是让页面上*.alibaba.com域名下的a链接都带上一个图标。那么可以写一些简单的qunit用例。首先在
test/grunt-demo-1.html准备好HTML用例:
1 2 3 4 5 6 7 8 9 | ... <div id="qunit-fixture"> <a href="#functional-link">#nogo</a> <a href="http://alibaba.com">grunt</a> <a href="http://china.alibaba.com">grunt</a> <a href="http://style.china.alibaba.com">grunt</a> <a href="http://q.pnq.cc">qhwa</a> </div> .... |
test/grunt-demo-1_test.js,写上我们自己的单元测试:
1 2 3 4 5 6 7 8 910 | // file: test/grunt-demo_test.js module('jQuery#ali-link', { setup: function() { this.elems = $('#qunit-fixture').children(); } }); test('is chainable', 1, function() { strictEqual(this.elems.alilink(), this.elems, 'should be chaninable'); }); test('can select', function() { equal( $('a:alilink', $('#qunit-fixture')).length, 3, 'should select links with alibaba domain' ); }); test('add icon to alibaba links', function() { this.elems.alilink(); this.elems.each(function(){ var self = $(this); if( /^http:\/\/((.+)\.)?alibaba\.com/.test(this.href) ) { ok(self.hasClass('ali-link')); } else { ok(!self.hasClass('ali-link')); } }); }); ... |
grunt qunit运行结果是这样的:
![](http://img03.taobaocdn.com/tfscom/T181NBFn4fXXaCwpjX.png)
各种报错,对吧?不要紧,因为我们还没有开始写代码。接下来我们的目标就是写功能代码,让这个测试通过。
经过一番敲击键盘,以及运行了几次
grunt qunit,终于,激动人心时刻到来了,我们通过了所有的单元测试:
![](http://img01.taobaocdn.com/tfscom/T1p5RDFn4bXXaCwpjX.png_620x10000.jpg)
最终的功能js如下:
1 2 3 4 5 6 7 8 910 | // file: src/grunt-demo-1.js (function($) { var ALI_LINK_REG = /^http:\/\/(.+\.)?alibaba\.com/; // Collection method. $.fn.alilink = function() { return this.each(function() { var self = $(this); if( isAliLink(this) ){ self.addClass('ali-link'); } else { self.removeClass('ali-link'); } }); }; // Custom selector. $.expr[':'].alilink = function(elem) { return isAliLink(elem); }; function isAliLink(el) { return /A/i.test(el.tagName) && ALI_LINK_REG.test(el.href); } }(jQuery)); |
功能已经完成了,但是插件就完成了吗?非也!
有了单元测试,我们可以放心重构我们的代码,达到最好的可维护性。先让机器帮我们做个code review——也就是linting。
准备好了,就来看下一招:
第三招:利用 lint 提高代码质量
运行命令grunt lint,果然有报警:
![](http://img03.taobaocdn.com/tfscom/T1qp8CFmXbXXaCwpjX.png_620x10000.jpg)
嗯,我们的正则表达式写法不太好,那就来优化一下吧!
又经过一阵修改,我们的代码终于在
grunt lint时没有报警了,不错!
![](http://img01.taobaocdn.com/tfscom/T1KuBqFhthXXaCwpjX.png_620x10000.jpg)
运行一下
grunt qunit,看到单元测试还能通过,功能正常,不错!
第四招:利用 watch 自动化
上面我们在重构和优化的时候,每次修改了功能js都要运行一遍grunt qunit和
grunt lint,grunt 可以帮我们做到一旦有修改,自动运行这两个命令,方便吧?
很简单,运行
grunt watch就好了。然后试一下修改 src/ 下面的 js,就不需要手动去运行qunit和lint任务了。 watch 就是这样的一个任务,监测一些文件,都有更新的时候,自动运行需要的任务。
![](http://img03.taobaocdn.com/tfscom/T1bb4BFodfXXaCwpjX.png_620x10000.jpg)
grunt 还自带了一些其他的任务,比如
用来压缩js和css的
min任务
用来开启一个本地http服务器的
server任务
用来将几个文件合并成一个的
concat任务
这些都是我们经常使用的任务。
小结
本文的示例代码已经放到 github 上。这一篇我们认识了 grunt ,下一篇将会看到 grunt 这种以任务为中心的设计,带来的强大扩展性。如果上面的例子已经让你感受到了无痛开发的乐趣,那么那些社区插件保证会让你更过瘾的!敬请期待本系列的第二篇—— 让 grunt 飞起来!
参考
grunt init相关文章推荐
- JavaScript实现35选7并记录历史状态
- css中兼容各個IE版本
- React Native Android增加本地图片
- JavaScript——正则表达式
- react-native-video error
- react-native run-android error
- ExtJs6 发送ajax如何动态改变发送参数的参数名的解决方法
- JS去除特殊字符
- Jquery局部刷新--多用于评论回复等
- js for循环 遍历数组 遍历对象属性
- 做前端开发,我只推荐这15个Chrome扩展[附带详细说明]
- AngularJS中Directive(指令)机制详解
- 依赖注入
- CSS3实现二级菜单
- JS检查是否为任意数(实数),正数,整数,正整数
- bootstrap row 下面的 col-md 高度相等 高度 一致 高度一样 有大用
- Converting and transferring data
- JavaScript(Iframe、window.open、window.showModalDialog)父窗口与子窗口之间的操作
- iOS - 定制多样式二维码
- javascript注入