本文轉載自:衆成翻譯
譯者:網絡埋伏紀事
連接:http://www.zcfy.cc/article/1754
原文:https://blog.risingstack.com/node-hero-node-js-unit-testing-tutorial/node
本教程將會學習 Node.js 中的單元測試是什麼,以及如何正確地測試你的應用程序。git
你能夠把測試看成你建立的應用程序的保障措施。他們將不只運行在你的本機上,還會在 CI 服務上,這樣失敗的構建就不會推送到產品系統中。github
你也許會問:個人應用程序中該測試什麼?我應該有多少測試?web
答案因情而異,可是根據經驗,你能夠遵循測試金字塔制定的準則。npm
基本上,測試金字塔描述你應該編寫單元測試、集成測試和端到端測試。集成測試要比端到端測試多,單元測試甚至要更多一些。json
下面咱們來看看如何爲應用程序添加單元測試!網絡
請注意,這裏咱們不打算討論集成測試和端到端測試,由於它們遠遠超出了本教程的範疇。app
*函數
編寫單元測試,是爲了看看給定的模塊(單元)是否工做。全部依賴都被剔除了,意味着咱們要爲模塊提供僞依賴。單元測試
應該爲指定模塊暴露的方法,而不是內部操做提供測試。
每一個單元測試有以下結構:
測試設置
調用被測試的方法
斷言
每一個單元測試應該只測試一個關注點。(固然,這不意味着你能夠只添加一個斷言)。
對於單元測試,咱們打算用以下模塊:
在動手寫單元測試以前,咱們先看看什麼是 spy、stub 和 mock!
可使用 spy 來獲取函數調用上的信息,好比函數被調用了多少次,或者傳遞了什麼參數給它們。
it('calls subscribers on publish', function () { var callback = sinon.spy() PubSub.subscribe('message', callback) PubSub.publishSync('message') assertTrue(callback.called) }) // 採用的示例來自於 sinon 文檔網站: http://sinonjs.org/docs/
Stub(樁)與 spy 相似,可是它是替換目標函數。可使用 stub 來控制一個方法的行爲,從而強制一個代碼路徑(好比拋出異常),或者阻止對外部資源的調用(好比 HTTP API)。
it('calls all subscribers, even if there are exceptions', function (){ var message = 'an example message' var error = 'an example error message' var stub = sinon.stub().throws() var spy1 = sinon.spy() var spy2 = sinon.spy() PubSub.subscribe(message, stub) PubSub.subscribe(message, spy1) PubSub.subscribe(message, spy2) PubSub.publishSync(message, undefined) assert(spy1.called) assert(spy2.called) assert(stub.calledBefore(spy1)) }) // 採用的示例來自於 sinon 文檔網站: http://sinonjs.org/docs/
mock 是帶有預先編好的行爲和指望值的僞方法。
it('calls all subscribers when exceptions happen', function () { var myAPI = { method: function () {} } var spy = sinon.spy() var mock = sinon.mock(myAPI) mock.expects("method").once().throws() PubSub.subscribe("message", myAPI.method) PubSub.subscribe("message", spy) PubSub.publishSync("message", undefined) mock.verify() assert(spy.calledOnce) // 採用的示例來自於 sinon 文檔網站: http://sinonjs.org/docs/ })
如你所見,對於 mock,你必須預先定義好指望的值。
*
假設要測試以下的模塊:
const fs = require('fs') const request = require('request') function saveWebpage (url, filePath) { return getWebpage(url, filePath) .then(writeFile) } function getWebpage (url) { return new Promise (function (resolve, reject) { request.get(url, function (err, response, body) { if (err) { return reject(err) } resolve(body) }) }) } function writeFile (fileContent) { let filePath = 'page' return new Promise (function (resolve, reject) { fs.writeFile(filePath, fileContent, function (err) { if (err) { return reject(err) } resolve(filePath) }) }) } module.exports = { saveWebpage }
這個模塊作一件事情:將網頁(基於指定的 URL)保存爲本機上的一個文件。要測試該模塊,咱們必須拔掉 fs
模塊和 request
模塊。
在咱們 RisingStack 團隊中,在真正開始爲本模塊編寫單元測試前,咱們一般添加一個 test-setup.spec.js
文件來作基礎測試設置,好比建立 sinon 沙箱。這樣能夠省下每次測試後編寫 sinon.sandbox.create()
和 sinon.sandbox.restore()
。
// test-setup.spec.js const sinon = require('sinon') const chai = require('chai') beforeEach(function () { this.sandbox = sinon.sandbox.create() }) afterEach(function () { this.sandbox.restore() })
此外,請注意,咱們老是將測試文件放在挨着實現文件的地方,因此就有了 .spec.js
這個名稱。在咱們的 package.json
文件中,能夠找到這些行:
{ "test-unit": "NODE_ENV=test mocha '/**/*.spec.js'", }
有了這些設置後,就能夠寫測試自己了!
const fs = require('fs') const request = require('request') const expect = require('chai').expect const webpage = require('./webpage') describe('The webpage module', function () { it('saves the content', function * () { const url = 'google.com' const content = '<h1>title</h1>' const writeFileStub = this.sandbox.stub(fs, 'writeFile', function (filePath, fileContent, cb) { cb(null) }) const requestStub = this.sandbox.stub(request, 'get', function (url, cb) { cb(null, null, content) }) const result = yield webpage.saveWebpage(url) expect(writeFileStub).to.be.calledWith() expect(requestStub).to.be.calledWith(url) expect(result).to.eql('page') }) })
完整的代碼庫在這裏找到:https://github.com/RisingStack/nodehero-testing
要了解你的代碼庫被測試覆蓋的狀況,你能夠生成一個覆蓋率報告。
這個報告將包含以下指標:
行覆蓋率
語句覆蓋率
分支覆蓋率
函數覆蓋率
在 RisingStack 公司中,咱們使用 istanbul 計算代碼覆蓋率。你應該將以下腳本添加到 package.json
文件中,來在 mocha
中使用 istanbul
:
istanbul cover _mocha $(find ./lib -name \"*.spec.js\" -not -path \"./node_modules/*\")
以後,你將獲得像這樣的代碼覆蓋率報告:
你能夠點擊一下,看看帶註解的源代碼 - 哪些部分被測試,哪些部分沒有。
測試能夠省下不少麻煩 - 不過,依然不可避免要時常調試。下一章將學習如何調試 Node.js 應用程序。