前言(个人见解)
随着前端工作量、代码量增多,如何保证代码质量,在angular、react、vue等多种mvvm框架的环境下,框架有自己的语法规则,即使每个人有每个人不同的编码风格,但用框架写出的代码都大同小异,代码质量上差距不太大,但在写一些类库,组件的时候必须要谨慎,因为组件类库的,会有同事,或者作为开源,给大家使用的,尽量避免bug的出现,这个时候需要代码健壮,需要产出高质量的代码,就必须要提升测试的力度,这个时候单元测试就派上用场了。
单元测试的原理
经常与单元测试联系起来的另外一些开发活动包括代码走读(Code review),静态分析(Static analysis)和动态分析(Dynamic analysis)。静态分析就是对软件的源代码进行研读,查找错误或收集一些度量数据,并不需要对代码进行编译和执行。动态分析就是通过观察软件运行时的动作,来提供执行跟踪,时间分析,以及测试覆盖度方面的信息。
使用技术
- Mocha:Javascript单元测试框架
- Chai:断言库,配合Mocha一起使用
下面案例所用mocha Api 总结
- 使用mocha测试异步代码,只需要在测试完成的时候调用一下回调函数即可。通过添加一个回调函数(通常命名为done)给it()方法,Mocha就会知道,它应该等这个函数被调用的时候才能完成测试。
describe('User', function() {
describe('#save()', function() {
it('should save without error', function() {
var user = new User('Luna')
user.save(function(err) {
if(err) done(err);
else done()
})
})
})
})
复制代码
- 同时,除了使用done()回调函数,你也可以返回一个Promise。这种方式对于测试那些返回promies的方法是实用的。
beforeEach(function() {
return db.clear()
.then(function() {
return db.save([tobi, loki, jane]);
});
});
describe('#find()', function() {
it('respond with matching records', function() {
return db.find({ type: 'User' }).should.eventually.have.length(3);
});
});
复制代码
下面案例所用chaiJS Api 总结
语言链
以下是作为可链接的getter提供,以提高断言的可读性。
- to
- be
- been
- is
- that
- which
- and
- has
- have
- with
- at
- of
- same
.equal(val [,msg])
- @param {Mixed} val
- @param {String} msg optional
expect(1).to.equal(1);
expect('foo').to.equal('foo');
复制代码
在链中较早添加以使用深度相等。有关深度相等算法的信息,请参阅 项目页面:https://github.com/chaijs/deep-eql。.deepdeep-eql
// Target object deeply (but not strictly) equals `{a: 1}`
expect({a: 1}).to.deep.equal({a: 1});
expect({a: 1}).to.not.equal({a: 1});
// Target array deeply (but not strictly) equals `[1, 2]`
expect([1, 2]).to.deep.equal([1, 2]);
expect([1, 2]).to.not.equal([1, 2]);
复制代码
一个简单小实例
- 全局安装Mocha
npm install --global mocha
- 安装chai
npm install --save-dev chai
- 创建项目目录结构
|—— lib // 功能js的文件夹
|—— tests // 测试用例js的文件夹
|—— index.js // 主要入口
|—— package.json // 依赖包信息
|—— README.md // 项目说明
复制代码
- 编写主要入口文件index.js
var file = require('./lib/file.js');
export default {
writeFile: file.writeFile,
readFile: file.readFile
}
复制代码
- 编写公共方法js (lib/utils.js)
var Promise = require("es6-promise").Promise;
/**
* 提供各种工具方法
* @type {{*}}
*/
module.exports = {
/**
* 获取Defer对象
* @return {[type]} [description]
*/
getDefer: function (){
var deferred = {};
deferred.promise = new Promise(function(resolve, reject){
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
},
/**
* promise when方法
* @param promises promise数组
* @returns {[type]} [description]
*/
when: function(promises) {
var deffered = this.getDefer();
Promise.all(promises).then(function(data) {
deffered.resolve(data);
}, function(err) {
deffered.reject(err);
});
return deffered.promise;
}
}
复制代码
- 编写读写文件方法 (lib/file.js)
var fs = require('fs');
var path = require('path');
var utils = require('./utils.js');
module.exports = {
/**
* 写文件
* @param file 文件路径
* @param data 数据
*/
writeFile: function(file, data) {
var deferred = utils.getDefer();
file = path.resolve(file);
fs.writeFile(file, data, 'utf-8', function(err) {
if(err){
deferred.reject(err);
}else {
deferred.resolve(true);
}
});
return deferred.promise;
},
/**
* 读文件
* @param file 文件路径
*/
readFile: function(file) {
var deferred = utils.getDefer();
file = path.resolve(file);
fs.readFile(file, 'utf-8', function(err, data) {
if(err){
deferred.reject(err);
}else {
deferred.resolve(data);
}
});
return deferred.promise;
}
};
复制代码
- 编写读写文件方法 (lib/file.js)
var fs = require('fs');
var path = require('path');
var utils = require('./utils.js');
module.exports = {
/**
* 写文件
* @param file 文件路径
* @param data 数据
*/
writeFile: function(file, data) {
var deferred = utils.getDefer();
file = path.resolve(file);
fs.writeFile(file, data, 'utf-8', function(err) {
if(err){
deferred.reject(err);
}else {
deferred.resolve(true);
}
});
return deferred.promise;
},
/**
* 读文件
* @param file 文件路径
*/
readFile: function(file) {
var deferred = utils.getDefer();
file = path.resolve(file);
fs.readFile(file, 'utf-8', function(err, data) {
if(err){
deferred.reject(err);
}else {
deferred.resolve(data);
}
});
return deferred.promise;
}
};
复制代码
- 编写utils测试 (lib/utils.js)
var utils = require('../lib/utils.js');
var expect = require('chai').expect;
describe('utils:工具方法测试', function() {
//defer对象测试
describe('utils.getDefer', function() {
var deferred = utils.getDefer();
it('defer成功', function() {
deferred.resolve(true);
return deferred.promise.then(function(data) {
expect(data).to.be.equal(true);
});
});
it('defer失败', function() {
deferred.reject(true);
return deferred.promise.then(function() {}, function(data) {
expect(data).to.be.equal(true);
});
});
});
//when测试
describe('utils.when', function() {
var deferred1 = utils.getDefer();
var deferred2 = utils.getDefer();
var deferred3 = utils.getDefer();
var deferred4 = utils.getDefer();
it('when成功', function() {
deferred1.resolve(true);
deferred2.resolve(true);
return utils.when([deferred1.promise, deferred2.promise]).then(function(data) {
expect(data).to.be.deep.equal([true, true]);
});
});
it('when失败', function() {
deferred3.resolve(true);
deferred4.reject(false);
return utils.when([deferred3.promise, deferred4.promise]).then(function() {},function(data) {
expect(data).to.be.equal(false);
});
});
});
});
复制代码
- 编写file测试 (lib/file.js)
var file = require('../lib/file.js');
var expect = require('chai').expect;
describe('file:功能测试', function() {
//writeFile功能测试
describe('file.writeFile', function() {
it('写文件:成功', function() {
var path = 'README.md';
var data = '说明文档';
return file.writeFile(path, data).then(function(flag) {
expect(flag).to.be.equal(true);
});
});
it('写文件:失败', function() {
var path = 'write-test.txt';
var data = '我是写入的数据';
return file.writeFile(path, data).then(function(){}, function(err) {
expect(true).to.be.equal(true);
});
});
});
//readFile功能测试
describe('file.readFile功能测试', function() {
it('读文件:成功', function() {
var path = 'README.md';
return file.readFile(path).then(function(data) {
expect(data).to.be.equal('说明文档');
});
});
it('读文件:失败', function() {
var path = 'write-test.txt';
return file.readFile(path).then(function(){}, function(err) {
expect(true).to.be.equal(true);
});
});
});
});
复制代码