像我这样优秀的猿,本该代码敲一生,怎么我的 Github 上,还是没有一个好项目?。
像我这样的人,遇见不爽,一言不合就要造轮子,开了好多的坑,很多临时的项目,为了 A 而诞生 B,所以也就只能保正在相关项目中可用,如果有另一个项目要调用时,都是提心吊胆的,生怕越写越不好收场。
最近决定,把自己的代码都好好整理一下,能发布的也别让它就安息了,怎么说它也是我思想逻辑的延续,亲生的啊。
这篇也算是我 blog 的第一篇文章了,为什么写了这个题目,因为这是我遇到的第一个问题,参观了一些知名项目的单元测试,看的我泪流满面的,这工作量可是感人啊,还有简单看了现在比较流行的单元测试库,API 也太丰富了吧,并且也太不好用了吧,所以我这脾气怎么能忍,当时就决定造新轮子。
我理解的单元测试功能,小项目友好的,能让你知道修改了代码后,是否还能正常运行就可以了,仅此而已,没有更多,那就开始介绍我的新坑吧,hi-test 单元测试库。
安装
$ npm install hi-test
复制代码
项目 github 地址:github.com/wl879/hi-te…
API 我就不在这理堆列了,也没什么功能,直接介绍使用。
使用
0. 查看项目的API
$ hi-test scan <path>
复制代码
这条命令会扫描目录中所有 js 文件的 exports, 并列出来,算是一个辅助功能
1. 开始写测试代码
先来创建一个模块,功能为拆分字符串,但需要跳过转义字符
exports.explode = function (str, separator) {
let re = new RegExp('(\\\\+)?\\' + separator);
let offset = 0;
let ref = [];
let m;
while (m = str.substr(offset).match(re) ) {
let esc = m[1] ? m[1].length : 0;
if (!esc || esc % 2 === 0) {
ref.push(str.substr(0, offset + m.index + esc));
str = str.substr(offset + m.index + m[0].length);
offset = 0;
} else {
offset += m.index + m[0].length;
}
}
ref.push(str);
return ref;
};
复制代码
接下来写测试代码,如下
const test = require('hi-test');
const explode = require('./index.js').explode;
test('测试 "无"转义符', (t)=>{
t.log(explode('hello, world', ','));
});
test('测试 "有"转义符', (t) => {
t.log(explode('hello\\, world', ','));
});
test('测试 "多"转义符', (t) => {
t.log(explode('hello\\\\\, world', ','));
t.log(explode('hello\\\\\\, world', ','));
});
复制代码
运行后输出如下,检查一下,输出是否是对的!嗯,是对的!!
这里的工作逻辑,是使用 t.log()
输出你想要测试的结果,t.log
不同于 console.log
输出会更详细,还有另一个函数 t.logp()
会更详细一点,包括变量的类型。
提示:如果变量中包含随机数或时间戳,或类名称的改变,会对测试有点麻烦,原因请往下看。
2. 如果输出是对的,还须要把 hash 码还给我
到这里,测试所写的代码以完成 99% 了,是不是没有那些 equal
/deepEqual
的API,感觉不像测试的正确用法,是不是上当了,这不就是普通调试时写的代码吗,对与错,基本靠瞅。
是的,很可惜,还不够智能,第一次的输出只能靠瞅来判断对错,好在你只需要瞅一次,如果输出结果是对的,你还须要这样做。
调用 hi-test
的 enable
方法.
var test = require('hi-test').enable();
复制代码
这时的输出就会是这样了,格式有点熟悉了
调用 enable
方法,开启 hi-test 后,会有以下改变:
- 屏蔽掉无用的输出
- 自动添加 hash 码
What?? 什么是 hash 码,什么鬼。
查看一下,在上面的示例的输出中,在 [Test Done]...
那一行,Title 后面会跟着一串(黄色的) hash 码,这是由捕获到的输出生成的 md5 值,hi-test 也就是通过它来判断测试是否正确的。
如果程序正确,它的每次的输出应该是一致的,所以反过来,通过判断两次输出是否一致,也就可以判断程序是否正确。当然这不能适用所有场景,但对于简单的测试还是基本靠谱的(相较于感人的测试代码量)。
手动添加 hash 码
只需要将 hash 码做为第二个参数加到 test 函数中即可
test('测试 "无"转义符', 'ff34b7', (t)=>{
....
})
复制代码
以上就是一个测试的全部流程了,是不是够简化的了。
3. 其它你还需要了解一下的
这里还要特殊的说明一点,test 间是阻塞执行的,只有上一个 done 之后,后面的才会执行,而 t.done()
是可以自动调用的,是通过监听 process.on('beforeExit') 来实现,所以一般情况下可以不用手动调用,这样它会在对的时间点被调用。
还有一种情况,如果调用的 func 是 aync function , 这将会在 func 结束时隐式调用 t.done()
, 这种情况适用于以下例子:
var test = require('hi-test')
let timing = setInterval(()=>{
console.log('Hi, See you again!');
}, 1000);
test('start', 'e893ed', async (t) => {
t.log('Hello, Start');
});
test('end', 'd41d8c', (t) => {
clearInterval(timing);
t.log('Hello, End');
)};
复制代码
例子中,如果不手动调用 t.done
,或用 async function 隐式调用,程序会一直的输出 See you again
。
如果你就是需要并行执行测试,可以这样写:
const test = require('hi-test');
const explode = require('./index.js').explode;
test('测试 "无"转义符').run((t)=>{
t.log(explode('hello, world', ','));
});
test('测试 "有"转义符').run((t) => {
t.log(explode('hello\\, world', ','));
});
复制代码
筛选输出
每一个 test 的输出中,都会有一个标识,看这里
其中 7e55
就是标识,它是由 md5(title) 得来的,所以当你只想查看某一项测试输出时,你可以用它来筛选,像这样
$ node ./test.js | grep '7e55'
复制代码
$ hi-test ./tset.js -df '测试 "无"转义符'
复制代码
测试垃圾回收
这里有一个实用的小功能,可以测试某一个变量是否被回收
var test = require('hi-test').enableGC();
var num = [1, 2, 3];
test('CG', (t)=>{
t.gc(num, 'num');
// num = null;
});
test('Next', (t) => {
console.log(num);
});
复制代码
如果变量 num
被回收则输出
[Test GC ] [Yes] >> num
复制代码
未被回收的话
[Test GC ] [No] >> num
复制代码
4. 开始测试吧
最后介绍一下 hi-test 的命令行,直接看帮助
Usage:
hi-test <files> [-d] [-a] [-f <title>]
Options:
+scan <path> Scan module exports
-f, --filter <title> Filter the output
-d, --debug Display all output
-a, --auto Automation
-v, --version Display version
-h, --help Display help
复制代码
在 package.json 中添加上这些一段
"scripts": {
"test": "hi-test tests/test-*.js -a",
}
复制代码
收工!!