Node.js 性能調優之調試篇(三)——三款實用調試工具

上一篇文章講解了如何用使用 VS Code 調試 Node.js 代碼,可以看出使用 VS Code 基本能覆蓋大部分的調試需求,但調試不只是打斷點,比如:

  • 如何快速的切換輸出的日誌類型(或級別)?
  • 我只想試一下用 moment 列印出年份是 moment().format(YYYY) 還是 moment().format(yyyy) 還是兩種寫法都可以?
  • 斷言報錯:AssertionError: false == true,沒啥有用信息,黑人問號???

這篇文章將安利給大家 3 款實用的調試工具,分別解決以上 3 種情況,提高我們的調試效率。

debug

debug 是一個小巧卻非常實用的日誌模塊,可以根據環境變數決定列印不同類型(或級別)的日誌。直接上代碼:

test/app.js

const normalLog = require(debug)(log)nconst errorLowLog = require(debug)(error:low)nconst errorNormalLog = require(debug)(error:normal)nconst errorHighLog = require(debug)(error:high)nnsetInterval(() => {n const value = Math.random()n switch (true) {n case value < 0.5: normalLog(value); breakn case value >= 0.5 && value < 0.7: errorLowLog(value); breakn case value >= 0.7 && value < 0.9: errorNormalLog(value); breakn case value >= 0.9: errorHighLog(value); breakn default: normalLog(value)n }n}, 1000)n

上面代碼每一秒生成一個隨機數,根據隨機數的值模擬不同級別的日誌輸出:

  • < 0.5:正常日誌
  • 0.5~0.7:低級別的錯誤日誌
  • 0.7~0.9:一般級別的錯誤日誌
  • >= 0.9:嚴重級別的錯誤日誌

運行:

$ DEBUG=* node app.jsn

列印如下:

可以看出,debug 模塊列印的日誌相比較於 console.log,有以下幾個特點:

  1. 不同的日誌類型分配了不同的顏色加以區分,更直觀了
  2. 添加了日誌類型的前綴
  3. 添加了自上一次該類型日誌列印到這次日誌列印經歷了多長時間的後綴

debug 模塊支持以下用法:

  • DEBUG=*:列印所有類型的日誌
  • DEBUG=log:只列印 log 類型的日誌
  • DEBUG=error:*:列印所有以 error: 開頭的日誌
  • DEBUG=error:*,-error:low:列印所有以 error: 開頭的並且過濾掉 error:low 類型的日誌,即這裡只列印一般級別和嚴重級別的錯誤日誌

下面演示一下第 4 種的用法,運行:

$ DEBUG=error:*,-error:low node app.jsn

列印如下:

repl2

我們在寫代碼的時候,有時候記不太清某個模塊某個方法的具體用法,比如:moment 列印出年份是 moment().format(YYYY) 還是 moment().format(yyyy) 還是兩種寫法都可以?這個時候相比較於翻閱官網網站,在 REPL 里試一下可能來得更快,通常步驟是:

$ npm i momentn$ noden> const moment = require(moment)n> moment().format(YYYY)n2017n> moment().format(yyyy)nyyyyn

一次還好,這樣用次數多了也略微繁瑣。幸好,我們可以使用 repl2 模塊解決這個問題。

repl2 顧名思義是 REPL 的增強版,repl2 會根據一個用戶配置(~/.noderc),預先載入模塊到 REPL 中,省下了我們手動在 REPL 中 require 模塊的過程。

全局安裝:

$ npm i repl2 -gn

使用方式很簡單:

1. 將常用的模塊全局安裝,如:

$ npm i lodash validator moment -gn

2. 添加配置到 ~/.noderc:

{n "lodash": "__",n "moment": "moment",n "validator": "validator"n}n

3. 運行 noder:

? test nodern__ = [email protected] -> localnmoment = [email protected] -> globalnvalidator = [email protected] -> globaln> moment().format(YYYY)n2017n> __.random(0, 5)n3n> validator.isEmail([email protected])ntruen

有幾點需要講解下:

  1. .noderc 是一個 JSON 文件,key 是模塊名字,value 是 require 這個模塊後載入到 REPL 中的變數名
  2. 這裡給 lodash 命名的變數名是 __ 而不是 _,是因為 REPL 中 _ 有特殊含義,表示上一個表達式的結果
  3. repl2 會優先載入當前目錄下的模塊,沒有找到然後再去載入全局安裝的模塊。上面結果顯示 lodash 是從本地目錄載入的,因為 test 目錄下已經安裝了 lodash,其餘的模塊沒有從本地目錄找到則嘗試從全局 npm 目錄載入。如果都沒有找到,則不會載入

power-assert

我們常用的斷言庫有:

  • should.js
  • expect.js
  • chai

但這類斷言庫都有一些通病:

  1. 過分追求語義化,API 複雜
  2. 錯誤信息不足

直接看代碼:

test/test/app.js

const assert = require(assert)nconst should = require(should)nconst expect = require(expect.js)nnconst tom = { id: 1, age: 18 }nconst bob = { id: 2, age: 20 }nndescribe(app.js, () => {n it(assert, () => {n assert(tom.age > bob.age)n })n it(should.js, () => {n tom.age.should.be.above(bob.age)n })n it(expect.js, () => {n expect(tom.age).be.above(bob.age)n })n})n

運行:

$ mochan

結果如下:

app.jsn 1) assertn 2) should.jsn 3) expect.jsnn 0 passing (13ms)n 3 failingnn 1) app.js assert:nn AssertionError [ERR_ASSERTION]: false == truen + expected - actualnn -falsen +truenn at Context.it (test/app.js:10:5)nn 2) app.js should.js:n AssertionError: expected 18 to be above 20n at Assertion.fail (node_modules/should/cjs/should.js:275:17)n at Assertion.value (node_modules/should/cjs/should.js:356:19)n at Context.it (test/app.js:13:23)nn 3) app.js expect.js:n Error: expected 18 to be above 20n at Assertion.assert (node_modules/expect.js/index.js:96:13)n at Assertion.greaterThan.Assertion.above (node_modules/expect.js/index.js:297:10)n at Function.above (node_modules/expect.js/index.js:499:17)n at Context.it (test/app.js:16:24)n

可以看出,基本沒啥有用信息。

這個時候,power-assert 粉墨登場。power-assert 使用起來很簡單,理論上只用一個 assert 就可以了,而且可以無縫的遷移。修改 test/package.json,添加 scripts:

"scripts": {n "test": "mocha -r intelli-espower-loader test/*.js"n}n

安裝 power-assert 和 intelli-espower-loader,然後運行測試:

$ npm i power-assert intelli-espower-loader --save-devn$ npm test n

結果如下:

> [email protected] test /Users/nswbmw/Desktop/testn> mocha -r intelli-espower-loader test/*.jsnn app.jsn 1) assertn 2) should.jsn 3) expect.jsnn 0 passing (42ms)n 3 failingnn 1) app.js assert:nn AssertionError [ERR_ASSERTION]: # test/app.js:10nn assert(tom.age > bob.age)n | | | | |n | | | | 20n | | | Object{id:2,age:20}n | 18 falsen Object{id:1,age:18}nn + expected - actualnn -falsen +truen2) app.js should.js:n AssertionError: expected 18 to be above 20n ...n3) app.js expect.js:n Error: expected 18 to be above 20n ...n

錯誤信息非常直觀,根本不需要解釋了。有兩點需要說明下:

  1. mocha 需要引入 intelli-espower-loader,主要是轉譯代碼,轉譯之後 require(assert) 都不需要改
  2. intelli-espower-loader 可選地在 package.json 添加 directories.test 配置,如:

"directories": {n "test": "test/"n}n

如果沒有 directories.test 配置則默認是 "test/"。

參考鏈接

  • 可能是最好的 JS Assert 庫 - 皇帝的新衣
  • intelli-espower-loader

推薦閱讀:

深入 Promise(二)——進擊的 Promise
採用Symbol和process.nextTick實現Promise
《球球大作戰》源碼解析——(1)運行起來
nodeJS 2016年官方技術調查報告
node-webkit教程(11)Platform Service之shell

TAG:Nodejs | 测试 | DebugPodcast |