4.14.1 mocha 和 supertest

mochasuptertest 是常用的测试组合,通常用来测试 restful 的 api 接口,这里我们也可以用来测试我们的博客应用。
在 myblog 下新建 test 文件夹存放测试文件,以注册为例讲解 mocha 和 supertest 的用法。首先安装所需模块:

  1. npm i mocha supertest --save-dev

修改 package.json,将:

package.json

  1. "scripts": {
  2. "test": "echo \"Error: no test specified\" && exit 1"
  3. }

修改为:

  1. "scripts": {
  2. "test": "mocha test"
  3. }

指定执行 test 目录的测试。修改 index.js,将:

index.js

  1. // 监听端口,启动程序
  2. app.listen(config.port, function () {
  3. console.log(`${pkg.name} listening on port ${config.port}`)
  4. })

修改为:

  1. if (module.parent) {
  2. // 被 require,则导出 app
  3. module.exports = app
  4. } else {
  5. // 监听端口,启动程序
  6. app.listen(config.port, function () {
  7. console.log(`${pkg.name} listening on port ${config.port}`)
  8. })
  9. }

这样做可以实现:直接启动 index.js 则会监听端口启动程序,如果 index.js 被 require 了,则导出 app,通常用于测试。

找一张图片用于测试上传头像,放到 test 目录下,如 avatar.png。新建 test/signup.js,添加如下测试代码:

test/signup.js

  1. const path = require('path')
  2. const assert = require('assert')
  3. const request = require('supertest')
  4. const app = require('../index')
  5. const User = require('../lib/mongo').User
  6. const testName1 = 'testName1'
  7. const testName2 = 'nswbmw'
  8. describe('signup', function () {
  9. describe('POST /signup', function () {
  10. const agent = request.agent(app)// persist cookie when redirect
  11. beforeEach(function (done) {
  12. // 创建一个用户
  13. User.create({
  14. name: testName1,
  15. password: '123456',
  16. avatar: '',
  17. gender: 'x',
  18. bio: ''
  19. })
  20. .exec()
  21. .then(function () {
  22. done()
  23. })
  24. .catch(done)
  25. })
  26. afterEach(function (done) {
  27. // 删除测试用户
  28. User.deleteMany({ name: { $in: [testName1, testName2] } })
  29. .exec()
  30. .then(function () {
  31. done()
  32. })
  33. .catch(done)
  34. })
  35. after(function (done) {
  36. process.exit()
  37. })
  38. // 用户名错误的情况
  39. it('wrong name', function (done) {
  40. agent
  41. .post('/signup')
  42. .type('form')
  43. .attach('avatar', path.join(__dirname, 'avatar.png'))
  44. .field({ name: '' })
  45. .redirects()
  46. .end(function (err, res) {
  47. if (err) return done(err)
  48. assert(res.text.match(/名字请限制在 1-10 个字符/))
  49. done()
  50. })
  51. })
  52. // 性别错误的情况
  53. it('wrong gender', function (done) {
  54. agent
  55. .post('/signup')
  56. .type('form')
  57. .attach('avatar', path.join(__dirname, 'avatar.png'))
  58. .field({ name: testName2, gender: 'a' })
  59. .redirects()
  60. .end(function (err, res) {
  61. if (err) return done(err)
  62. assert(res.text.match(/性别只能是 m、f 或 x/))
  63. done()
  64. })
  65. })
  66. // 其余的参数测试自行补充
  67. // 用户名被占用的情况
  68. it('duplicate name', function (done) {
  69. agent
  70. .post('/signup')
  71. .type('form')
  72. .attach('avatar', path.join(__dirname, 'avatar.png'))
  73. .field({ name: testName1, gender: 'm', bio: 'noder', password: '123456', repassword: '123456' })
  74. .redirects()
  75. .end(function (err, res) {
  76. if (err) return done(err)
  77. assert(res.text.match(/用户名已被占用/))
  78. done()
  79. })
  80. })
  81. // 注册成功的情况
  82. it('success', function (done) {
  83. agent
  84. .post('/signup')
  85. .type('form')
  86. .attach('avatar', path.join(__dirname, 'avatar.png'))
  87. .field({ name: testName2, gender: 'm', bio: 'noder', password: '123456', repassword: '123456' })
  88. .redirects()
  89. .end(function (err, res) {
  90. if (err) return done(err)
  91. assert(res.text.match(/注册成功/))
  92. done()
  93. })
  94. })
  95. })
  96. })

此时编辑器会报语法错误(如:describe 未定义等等),修改 .eslintrc.json 如下:

  1. {
  2. "extends": "standard",
  3. "globals": {
  4. "describe": true,
  5. "beforeEach": true,
  6. "afterEach": true,
  7. "after": true,
  8. "it": true
  9. }
  10. }

这样,eslint 会忽略 globals 中变量未定义的警告。运行 npm test 看看效果吧,其余的测试请读者自行完成。

4.14.2 测试覆盖率

我们写测试肯定想覆盖所有的情况(包括各种出错的情况及正确时的情况),但光靠想需要写哪些测试是不行的,总也会有疏漏,最简单的办法就是可以直观的看出测试是否覆盖了所有的代码,这就是测试覆盖率,即被测试覆盖到的代码行数占总代码行数的比例。

注意:即使测试覆盖率达到 100% 也不能说明你的测试覆盖了所有的情况,只能说明基本覆盖了所有的情况。

istanbul 是一个常用的生成测试覆盖率的库,它会将测试的结果报告生成 html 页面,并放到项目根目录的 coverage 目录下。首先安装 istanbul:

  1. npm i istanbul --save-dev

配置 istanbul 很简单,将 package.json 中:

package.json

  1. "scripts": {
  2. "test": "mocha test"
  3. }

修改为:

  1. "scripts": {
  2. "test": "istanbul cover _mocha"
  3. }

即可将 istanbul 和 mocha 结合使用,运行 npm test 终端会打印:

测试 - 图1

打开 myblog/coverage/Icov-report/index.html,如下所示:

测试 - 图2

可以点进去查看某个代码文件具体的覆盖率,如下所示:

测试 - 图3

红色的行表示测试没有覆盖到,因为我们只写了 name 和 gender 的测试。