node如何开始单测?单测有何意义?node如何使用mock?node如何断言?如何记录node测试问题报告等相关问题,以下便是本节导航,源码可在这获取 :
gitee: https://gitee.com/xifeng-canyang/node-deep/tree/master/unitTest
- 单元测试
- 单测遵循的规则
- 单测内容
- 测试方法
- 测试方法
- moach记录异常
- 集成
- 使用方式
- 超时
- mode的mock实现
- 私有方法测试
单元测试
node的可行性,从面对问题由被动到主动,单测起到了至关重要的作用;测试的意义在于,用户消费产出的代码之前,开发者
单测遵循的规则
- 单一原则
- 接口抽象
- 层次分离
对于开发者而言,不仅要编写单元测试,还应当编写测试代码。
单测内容
包括断言、测试框架、测试用例、测试覆盖率、mock和持续集成等。
测试方法
这里介绍assert:const assert = require('assert');
- ok() 测试结果为真
- equal() 浅期望相等
- notEqual() 浅期望不等
- deepEqual() 深期望相等
- notDeepEqual() 深期望不等
- strictEqual() 严格相等
- notStrictEqual() 严格不等
- throws() 是否抛出错误
- doesNotThrow() 是否不抛出错误
- ifError() 判断是否为假值(null undefined)
以上断言内容来自node本身的包,日常我们一般使用第三方包,这里推荐chai,他包含了expect和assert以及should,因此功能强大。
文档地址:https://www.chaijs.com/api/
我们新建文件tests/basic.js
const assert = require('chai').assert;
describe('basic test',() => {
it ('test assert',() => {
try {
// AssertionError [ERR_ASSERTION]: 2 == 21
assert.equal(Math.max(1,2),2);
// asserts value is null | undefined
assert.ifError(null)
} catch(err) {
console.log(err,'AssertionError')
}
})
});
moach记录异常
node的测试报告我们借助来第三方框架moach,记录异常,程序继续执行,最终生成测试报告;用于管理测试用例和生成测试报告。
集成
npm i moach -D
使用方式
在scritps中配置blanket,其中pattern路径为当前文件编译的跟路径;--watch表示Mocha会监视源代码和测试代码的更改,每次更改之后重新测试;-- recursive指明会找到根目录下的子目录的测试代码并运行。
"scripts": {
"blanket": {
"pattern": "tests"
},
"test": "echo \"Error: no test specified\" && exit 1",
"coverage": "mocha tests --reporter mochawesome",
"mocha": "mocha tests --recursive --watch"
}
通过--reporter + name,当测试跑通之后会生成一个name-report的文件夹,我们在浏览器打开其中的html文件即可看到测试报告
超时
测试用例的it回调函数中包含一个done,当需要执行超时的时候注意执行done()。
- mocha的超时默认是2000毫秒,我们也可以在测试用例it中,通过this.timeout(xx)设置超时时间;
- 也可以在每个层describe下面通过this.timeout(xx),为层下面的所有测试用例一同设置超时时间;
- 我们也可以通过mocha -t <ms> 设置所有用例的超时时间。
我们新建测试文件tests/timeout/index.js
const should = require('should');
const parseAsync = (input,callback) => {
setTimeout(() => {
let result ;
try {
result = JSON.parse(input)
} catch(err) {
return callback(err)
}
callback(null,result)
}, 100);
}
describe('timeoute',function() {
// 设置超时时间
this.timeout(3500)
it('parse async should be ok',(done) => {
parseAsync('{"name": "jack tian"}', (err,data) => {
should.not.exist(err);
data.name.should.be.equal('jack tian');
done();
})
})
})
mode的mock实现
这里主要对同步api和异步api进行模拟
我们新建文件utils/basic.js
const fs = require('fs');
const getContent = (filename) => {
try {
return fs.readFileSync(filename,'utf-8')
} catch(err) {
return '';
}
}
const getReadFile = (path) => {
return new Promise((resolve,reject) => {
fs.readFile(path,'utf-8',(err,data) => {
if (err) reject(err);
resolve(data)
})
})
}
module.exports = {
getContent,
getReadFile
}
两个方法,分别演示了node如何mock同步函数和mock异步函数。
针对同步函数处理方法
let _readFileSync;
before(function() {
_readFileSync = fs.readFileSync;
/**
* 这里我们模拟,当输入的文件名不是abc.txt的时候正常返回,而等于abc.txt的时候抛出错误
*/
fs.readFileSync = function(filename,encoding) {
if (filename === 'abc.txt') {
throw new Error('mock read file sync');
} else {
return 'hello txt'
}
}
})
after(function() {
fs.readFileSync = _readFileSync;
})
针对异步函数处理方法
let _readFile;
before(function() {
_readFile = fs.readFile;
fs.readFile = function(path,encoding,callback) {
if ( path === 'aaa.txt') {
process.nextTick(() => {
callback(null,'aaa.txt')
})
} else {
process.nextTick(() => {
callback(new Error('mock read file'))
})
}
}
after(function() {
fs.readFile = _readFile;
})
})
这里需要注意两点
1.由于readFile是异步,因此需要使用process.nextTick立即执行
2.由于readFile包含一个callback,并且有两个参数,一个err,一个data,因此我们callback回调的时候,如果路径正常,那么我们第一个参数需要给null/undefined/'',让他不会抛出错误,从而返回第二个参数data的值
完整的例子
const { parseAsync,getContent,getReadFile } = require('../utils/basic');
const should = require('should');
const fs = require('fs');
describe('parse async',() => {
let _readFileSync;
let _readFile;
before(function() {
_readFileSync = fs.readFileSync;
fs.readFileSync = function(filename,encoding) {
if (filename === 'abc.txt') {
throw new Error('mock read file sync');
} else {
return 'hello txt'
}
}
_readFile = fs.readFile;
fs.readFile = function(path,encoding,callback) {
if ( path === 'aaa.txt') {
process.nextTick(() => {
callback(null,'aaa.txt')
})
} else {
process.nextTick(() => {
callback(new Error('mock read file'))
})
}
}
})
after(function() {
fs.readFileSync = _readFileSync;
fs.readFile = _readFile;
})
it('test get read file',async function() {
try {
const r11 = await getReadFile('ww')
console.log(r11,'r11')
} catch(err) {
console.log(err,'error: test get read file')
}
try {
const r11 = await getReadFile('aaa.txt')
console.log(r11,'r11')
} catch(err) {
console.log(err,'error: test get read file')
}
})
it ( 'test getContent',function (){
try {
const r1 = getContent('basic.test.js11')
console.log(r1,'r1');
const r2 = getContent('abc.txt');
console.log(r2,'r2')
} catch(err) {
console.log(err,'err')
}
})
})
私有方法测试
node的私有方法测试我们需要借助第三方的rewire,因此需要先安装
npm i rewrie -D
官方地址:https://github.com/jhnns/rewire
我们新建文件utils/private.js
const limit = (num) => {
return num > 10 ? num : '0' + num;
}
const getMonth = () => {
const num = Math.floor(5 + Math.random(10))
return num;
}
然后新建测试tests/private.js
const rewire = require('rewire');
const should = require('chai').should;
describe('test private',() => {
it('test limit',() => {
const lib = rewire('../utils/private.js');
const limit = lib.__get__('limit');
limit(2).should.be.equal('02');
})
})
核心便是get方法,更多内容请参考官网。
以上便是node测试相关,实不相瞒,想要您一个赞~