|
| 1 | +--- |
| 2 | +title: CLI 测试:文件与标准输出 |
| 3 | +tags: BDD Mocha Node.js 测试 Ramdisk |
| 4 | +--- |
| 5 | + |
| 6 | +在 [利用 Mocha 进行 BDD 风格测试](/2016/06/23/mocha-chai-bdd.html) 中介绍了 |
| 7 | +Node.js 下如何做单元测试,要确保软件的质量我们还需要 e2e 测试, |
| 8 | +确保整个系统在真实场景下能够正常工作。 |
| 9 | + |
| 10 | +> End-to-end testing involves ensuring that the integrated components of an application function as expected. The entire application is tested in a real-world scenario such as communicating with the database, network, hardware and other applications. -- [techopedia][techopedia] |
| 11 | +
|
| 12 | +本文 Node.js 的命令行程序(CLI)为例,介绍如何测试一个可执行程序的输入输出以及文件修改。 |
| 13 | +样例代码可参考 [APM 的 e2e 测试代码][apm]。 |
| 14 | + |
| 15 | +<!--more--> |
| 16 | + |
| 17 | +# 输入输出 |
| 18 | + |
| 19 | +测试一个 cli 程序的输入输出很容易,几乎不需要借助任何工具。 |
| 20 | +使用内置的 `child_process` 模块就足够了: |
| 21 | + |
| 22 | +```javascript |
| 23 | +import {exec} from 'child_process' |
| 24 | + |
| 25 | +it('should print author', function (done) { |
| 26 | + exec('./bin/cli --author', |
| 27 | + (err, stdout, stderr) => err ? done(err) : expect(stdout).to.equal('http://harttle.com') |
| 28 | + ) |
| 29 | +}) |
| 30 | +``` |
| 31 | + |
| 32 | +如果希望注入环境变量,修改要执行的命令即可: |
| 33 | + |
| 34 | +```javascript |
| 35 | +exec('PORT=8080 ./bin/cli --start') |
| 36 | +``` |
| 37 | + |
| 38 | +为了测试代码更加可读,可以引入 `chai-as-promise`。上述测试项可以重写为: |
| 39 | + |
| 40 | +```javascript |
| 41 | +import Promise from 'bluebird' |
| 42 | + |
| 43 | +it('should print author', function () { |
| 44 | + var p = Promise.fromCallback(cb => exec('./bin/cli --author', cb)) |
| 45 | + return expect(p).to.eventually.equal('http://harttle.com') |
| 46 | +}) |
| 47 | +``` |
| 48 | + |
| 49 | +> 更多细节请参考:[Mocha 下测试异步代码](/2016/07/12/async-test-with-chai-as-promised.html) 一文。 |
| 50 | +
|
| 51 | +# 文件系统 |
| 52 | + |
| 53 | +在单元测试中,我们可以 [修改内置的 `fs` 模块][mock-fs] 达到测试的目的。 |
| 54 | +其实文件并没有真正被读写,只是测试了调用读写的逻辑是否正确。 |
| 55 | +但 e2e 测试中命令行程序在不同的进程启动,无法修改其中的 `fs` 模块。 |
| 56 | +当然你可以 `require` 进来去执行,但那就不是 e2e 测试了。 |
| 57 | + |
| 58 | +在此 [Harttle](http://harttle.com) 介绍两种方式来初始化用于测试的工作区。 |
| 59 | + |
| 60 | +## 临时目录 |
| 61 | + |
| 62 | +如果文件读写较少,可以在测试开始前初始化一个临时目录,测试完成时删除。 |
| 63 | +使用 [fs-extra][fs-extra] 会让这个工作变得非常简单: |
| 64 | + |
| 65 | +```javascript |
| 66 | +import fs from 'fs-extra' |
| 67 | +import os from 'os' |
| 68 | + |
| 69 | +beforeEach(() => fs.emptyDir(os.tmpdir() + '/test')) |
| 70 | +``` |
| 71 | + |
| 72 | +## Ramdisk |
| 73 | + |
| 74 | +也可以把一部分内存挂载到文件系统,有大量文件读写时比较快,同时可以保护 SSD(是这样吗?)。 |
| 75 | +Node 下有一个不错的 [node-ramdisk][ramdisk] 工具来创建内存虚拟磁盘。 |
| 76 | + |
| 77 | +```javascript |
| 78 | +const ramdisk = require('node-ramdisk') |
| 79 | +var mountpoint |
| 80 | +var disk |
| 81 | + |
| 82 | +before(done => { |
| 83 | + disk = ramdisk(this.dirname) |
| 84 | + disk.create(10, function (err, mount) { // 创建 10MB 的虚拟磁盘 |
| 85 | + mountpoint = mount |
| 86 | + }) |
| 87 | +}) |
| 88 | +after(done => disk.delete(mountpoint, cb)) |
| 89 | +``` |
| 90 | + |
| 91 | +创建 ramdisk 系统调用比较耗时,建议在 `before()` 时完成,`beforeEach()` 时清空目录。 |
| 92 | +此外,因为设备根目录存在权限较高的隐藏文件,建议在 `mountpoint` 下创建一个子目录来进行测试。 |
| 93 | + |
| 94 | +# 网络 |
| 95 | + |
| 96 | +在单元测试中可以使用 [Sinon fakeXHR][fakeXHR] 或 [nock][nock] 来 Mock 掉 http 模块, |
| 97 | +但 e2e 测试中则需要启动真实的服务器。 |
| 98 | + |
| 99 | +```javascript |
| 100 | +var server |
| 101 | + |
| 102 | +before(done => { |
| 103 | + server = http.createServer(requestHandler) |
| 104 | + server.listen(8080, done)) |
| 105 | +}) |
| 106 | + |
| 107 | +after(done => server.close(done)) |
| 108 | + |
| 109 | +function requestHandler (req, res) { |
| 110 | + res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }) |
| 111 | + res.end('ok') |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +由于不同机器的环境可能不同,端口应该在置默认值的同时读取环境变量。 |
| 116 | +如果只需要静态文件服务,可以直接引入 [http-server][http-server] 模块。 |
| 117 | + |
| 118 | +[mock-fs]: /2016/08/01/javascript-mock-fs.html |
| 119 | +[techopedia]: https://www.techopedia.com/definition/7035/end-to-end-test |
| 120 | +[apm]: https://github.com/apmjs/apmjs/tree/master/test/e2e |
| 121 | +[fs-extra]: https://github.com/jprichardson/node-fs-extra |
| 122 | +[ramdisk]: https://www.npmjs.com/package/node-ramdisk |
| 123 | +[fakeXHR]: http://sinonjs.org/releases/v4.0.2/fake-xhr-and-server/ |
| 124 | +[nock]: https://github.com/node-nock/nock |
| 125 | +[http-server]: https://github.com/indexzero/http-server |
0 commit comments