上一小节讲解了如何用使用 VS Code 调试 Node.js 代码,但调试不只是打断点,比如:
moment().format('YYYY')
,还是 moment().format('yyyy')
,还是两种写法都可以?本节将介绍 3 款实用的调试工具,分别解决以上 3 种情况,来提高我们的调试效率。
debug 是一个小巧却非常实用的日志模块,可以根据环境变量决定打印不同类型(或级别)的日志。代码如下:
app.js
const normalLog = require('debug')('log')
const errorLowLog = require('debug')('error:low')
const errorNormalLog = require('debug')('error:normal')
const errorHighLog = require('debug')('error:high')
setInterval(() => {
const value = Math.random()
switch (true) {
case value < 0.5: normalLog(value); break
case value >= 0.5 && value < 0.7: errorLowLog(value); break
case value >= 0.7 && value < 0.9: errorNormalLog(value); break
case value >= 0.9: errorHighLog(value); break
default: normalLog(value)
}
}, 1000)
运行上面的代码,每一秒生成一个随机数,根据随机数的值模拟不同级别的日志输出:
运行:
$ DEBUG=* node app.js
打印如下:
可以看出,debug 模块打印的日志与 console.log 相比,有以下几个特点:
debug 模块支持以下用法:
下面演示一下第 4 种的用法,运行:
$ DEBUG=error:*,-error:low node app.js
打印如下:
我们在写代码时,有时可能记不清某个模块的某个方法的具体用法,比如:用 moment 格式化年份是用 moment().format('YYYY')
还是用 moment().format('yyyy')
还是两种写法都可以?lodash 的 _.pick
方法能否能接收数组作为参数?这个时候相对于翻阅官方文档,在 REPL 里试一下可能会更快,通常步骤是:
$ npm i moment
$ node
> const moment = require('moment')
> moment().format('YYYY')
'2017'
> moment().format('yyyy')
'yyyy'
一次还好,次数多了也略微烦琐。repl2 模块便是为了解决这个问题而生的。
repl2 顾名思义是 REPL 的增强版,repl2 会根据一个用户配置(~/.noderc),预先加载模块到 REPL 中,省下了我们手动在 REPL 中 require 模块的过程。
全局安装 repl2:
$ npm i repl2 -g
使用方式很简单:
$ npm i lodash validator moment -g
{
"lodash": "__",
"moment": "moment",
"validator": "validator"
}
$ noder
__ = lodash@4.17.4 -> local
moment = moment@2.18.1 -> global
validator = validator@7.0.0 -> global
> moment().format('YYYY')
'2017'
> __.random(0, 5)
3
> validator.isEmail('foo@bar.com')
true
需要讲解以下几点:
我们常用的断言库有:
但这类断言库都有一些通病:
先看一段代码:
test.js
const assert = require('assert')
const should = require('should')
const expect = require('expect.js')
const tom = { id: 1, age: 18 }
const bob = { id: 2, age: 20 }
describe('app.js', () => {
it('assert', () => {
assert(tom.age > bob.age)
})
it('should.js', () => {
tom.age.should.be.above(bob.age)
})
it('expect.js', () => {
expect(tom.age).be.above(bob.age)
})
})
运行:
$ mocha
结果如下:
app.js
1) assert
2) should.js
3) expect.js
0 passing (13ms)
3 failing
1) app.js
assert:
AssertionError [ERR_ASSERTION]: false == true
+ expected - actual
-false
+true
at Context.it (test.js:10:5)
2) app.js
should.js:
AssertionError: expected 18 to be above 20
at Assertion.fail (node_modules/should/cjs/should.js:275:17)
at Assertion.value (node_modules/should/cjs/should.js:356:19)
at Context.it (test.js:13:23)
3) app.js
expect.js:
Error: expected 18 to be above 20
at Assertion.assert (node_modules/expect.js/index.js:96:13)
at Assertion.greaterThan.Assertion.above (node_modules/expect.js/index.js:297:10)
at Function.above (node_modules/expect.js/index.js:499:17)
at Context.it (test.js:16:24)
可以看出,基本没有有用的信息。这时,power-assert 粉墨登场。
power-assert 使用起来很简单,理论上只用一个 assert 就可以了,而且可以无缝迁移。
注意:在使用 intelli-espower-loader 时,要求必须将测试文件放到 test/ 目录下,所以我们在 test 目录下创建 test/app.js,将原来的 test.js 代码粘贴过去。
安装 power-assert 和 intelli-espower-loader,然后运行测试:
$ npm i power-assert intelli-espower-loader --save-dev
$ mocha -r intelli-espower-loader
结果如下:
app.js
1) assert
2) should.js
3) expect.js
0 passing (42ms)
3 failing
1) app.js
assert:
AssertionError [ERR_ASSERTION]: # test/app.js:10
assert(tom.age > bob.age)
| | | | |
| | | | 20
| | | Object{id:2,age:20}
| 18 false
Object{id:1,age:18}
+ expected - actual
-false
+true
...
错误信息非常直观,有以下两点需要说明:
require('assert')
都不需要改。"directories": {
"test": "mytest/"
}
如果没有 directories.test 配置,则默认是 test/
。