###前言
这是一篇难产的博文,从去年写到了今年……
七月底到目前,已经使用了AngularJS开发了两三个项目。与之前使用的jQuery相比,Angualr非常强大,减少了一大堆的代码,印象最深的是它的双向数据绑定,一旦建立双向绑定,用户的任何变量操作都能立马同步到它绑定的变量,页面上的效果是这个值实时更新。这就意味着,当我们想要与后台服务器交互的时候,我们节省了收集数据的步骤,只需把绑定的对象放在参数里就行。
然而,AngularJS的强大不止如此。AngularJS的设计目标包括如下两点:
- 将应用逻辑与对DOM的操作解耦。这会提高代码的可测试性。
- 将应用程序的测试看的跟应用程序的编写一样重要。代码的构成方式对测试的难度有巨大的影响。
没错,AngularJS最强大的功能之一就是它的高可测试性。
我们在浏览AngularJS的官方文档的时候总能看到每个指令后面都会有一个测试代码片段。
###开始之前
下面我将编写一个简单的网页应用,将联系人存储到本地存储(LocalStorage)中。编写这个应用时,我将践行测试驱动开发(TDD)的精神。同时,路由导航方面,我不会用angular-router,而是使用ui-router。
###搭建项目
用bower
搭建项目框架:
mkdir karmaDmo
bower init
bower install bootstrap --save-dev
bower install angularjs --save-dev
bower install ui-router --save-dev
bower install angular-mocks --save-dev
初始化karma配置以及karma的使用,请参考我之前的文章 前端测试环境Karma简介。
###编写测试用例
Karma的测试用例语法是Jasmine。
describe('app.services test', function () {
var service;
beforeEach(function () {
//clear localStorage,inject the service before running test
if(window.localStorage) {
window.localStorage.clear();
}
module('app.services');
inject(function (storageService) {
service = storageService;
});
});
it('should be a string', function () {
expect(angular.isString(service.storageKey)).toBeTruthy();
});
it('should be a object', function () {
expect(angular.isObject(service.getInstance())).toBeTruthy();
});
it('should be an array', function () {
expect(angular.isArray(service.getAll())).toBeTruthy();
});
it('should be an empty array', function () {
expect(service.getAll().length).toEqual(0);
});
it('should be one item in the array', function () {
var item = {
name: 'Arthur',
mobile: '+86-13452121990'
};
service.save(item);
expect(service.getAll().length).toEqual(1);
});
});
###编写实现代码
启动Karma测试环境,然后编辑本app的业务逻辑:
angular.module('app.services', [])
.factory('storageService', function () {
var factory = {
storageKey: 'contactApp',
getInstance: function() {
if(window.localStorage) {
var storage = window.localStorage.getItem(factory.storageKey) || { author: 'Arthur', description: 'save contacts to localStorage', data: []};
return angular.fromJson(storage);
}else{
return;
}
},
saveInstance: function(instance){
var string = angular.toJson(instance);
window.localStorage.setItem(factory.storageKey, string);
},
getAll: function(){
var result = null,
instance = factory.getInstance();
if(instance) {
result = instance.data || [];
}else{
result = [];
}
return result;
},
save: function(item){
if(item.key) {
factory.update(item);
}else{
factory.add(item);
}
},
add: function(item){
var instance = factory.getInstance();
item.key = new Date().getTime();
instance.data.push(item);
factory.saveInstance(instance);
},
update: function(item){
var instance = factory.getInstance(),
datas = instance.data;
for(var i in datas) {
if(datas[i].key === item.key) {
datas[i] = item;
break;
}
};
factory.saveInstance(instance);
}
};
return factory;
});
编写完毕后,Karma监控台的所有所有测试用例都亮绿灯的话,表明业务逻辑代码都是OK的。
本文章例子的源代码都发布到Github上,戳此查看。
###一些思考
对于测试驱动开发的优劣点讨论这里不深入,但是有一点我记得很深刻。在最近的一个项目开发过程中,出现了一个bug让我们非常诧异的,因为之前的某个版本是正常的,但是在那个时候,它出问题了。我一位同事废了好大劲才发现这个bug是因为修改旧的bug才导致出现新的bug。于是我就思考,如果当时也运行着一套类似Karma这样的测试环境,我们肯定能避免这样子的问题出现的。
###参考