本文转载自:众成翻译
译者:网络埋伏纪事
连接: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 应用程序。