Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端单元测试 #110

Open
yanyue404 opened this issue Nov 29, 2019 · 0 comments
Open

前端单元测试 #110

yanyue404 opened this issue Nov 29, 2019 · 0 comments

Comments

@yanyue404
Copy link
Owner

yanyue404 commented Nov 29, 2019

前言

不是为了测试而测试,测试的目的是在于提高代码质量、降低错误

测试的类型:

  • 单元测试(unit testing)Mocha 关注输入与输出
  • 功能测试(feature testing)Nightmare 关注功能效果
  • 集成测试(integration testing)Travis CI 关注自动化流程
  • 测试覆盖率(test coverage) codecov.io 关注测试代码覆盖率,可以更好的反应单元测试的质量

单元测试 + 持续集成:mocha + travis-ci

Demo: addKey

编写测试用例:为数组类型的每一项添加新的键值对,并过滤子项:

require('mocha');
const assert = require('assert');
const addKey = require('./');

let arr = [
  {
    id: 1,
    name: '亚瑟',
  },
  {
    id: 2,
    name: '狄仁杰',
  },
  {
    id: 3,
    name: '曹操',
  },
];
describe('addKey', function () {
  it('should return add custom key:', function () {
    addKey(arr, { Address: 'Chinese' });

    assert(arr[0].Address === 'Chinese');
    assert(arr[1].Address === 'Chinese');
    assert(arr[2].Address === 'Chinese');
  });

  it('should return add custom key and filtered correctly:', function () {
    addKey(arr, { isShow: false }, (v, index, array) => {
      index === array.length - 1 ? ((v.name = '铠'), (v.isShow = true)) : '';
    });

    assert(!arr[0].isShow);
    assert(!arr[1].isShow);
    assert(arr[2].isShow); // true
  });

  it('should return omit custom key and filtered correctly:', function () {
    addKey(arr, (v, index, array) => {
      index === array.length - 1 ? ((v.name = '曹操'), (v.isShow = true)) : '';
    });

    assert(!arr[0].isShow);
    assert(!arr[1].isShow);
    assert(arr[2].isShow); // true
  });
});

package.json设置好npm script test测试钩子,在自己本地测试通过后再提交到 github。

{
  "scripts": {
    "test": "mocha"
  }
}

travis-ci 集成

打开 Travis 的官网,用你的 Github 账号登录之后,就可以打开 github 中的项目 addKey将它托管到平台上。紧接着在根目录中创建.travis.yml 文件,这个文件被称为 Travis 的配置文件,用来描述告诉工具在你提交代码之后如何执行集成工作。(官方文档

这里我们配置非常简单,告诉 Travis 我们使用的是 v6-v11 版本的 node.js,它会帮助我们在各个不同的 node 版本上测试代码的有效性

language: node_js
node_js:
  - 'node'
  - '11'
  - '10'
  - '9'
  - '8'
  - '7'
  - '6'

我们每次提交代码到 github,Travis CI 会检查 github 项目并发到 https://travis-ci.org/进行多版本测试, 当单个项目实例构建完成($ npm install)后,会自动执行 $ npm test钩子来校验代码的正确性。如果所有的版本实例都测试通过就会显示构建成功的徽章,否则则会发送右键告诉我们构建失败的原因(也可以进入官网的构建错误的实例进行查看失败原因)

构建结果:

NPM version Build Status

功能测试:mocha+nightmare

进行完整的页面级别功能测试,需要使用 nightmare , 它依靠 electron作为项目运行的容器进行测试。

解决国内安装 electron 困难的方法:

ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/" npm install nightmare -D

其他依赖项:

npm install mocha chai -D

测试一个分页组件是否成功渲染

scripts 命令编写

 "scripts": {
    "demos": "node ./test/server.js",
    "test": "mocha \"./test/**/*.test.js\"",
    "dev": "webpack-dev-server",
    "prettier": "prettier --write \"./src/**/*.{js,css,html}\"",
    "build": "webpack && gulp mini && npm run test"
  }

启动页面测试的 node 环境入口文件, http://127.0.0.1:3000/pagination 为分页组件渲染结果

const express = require('express');
const path = require('path');
const app = express();
var port = process.env.PORT || 3000;

app.use('/style', express.static(path.resolve(__dirname, '../style')));
app.use('/dist', express.static(path.resolve(__dirname, '../dist')));
app.use('/src', express.static(path.resolve(__dirname, '../src')));

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'index.html'));
});
// List
app.get('/pagination', (req, res) => {
  res.sendFile(path.join(__dirname, '/pagination/test.html'));
});

app.listen(port, function () {
  console.log(`app listening on port http://127.0.0.1:${port}`);
});

测试方法,组件是否渲染成功,仅测试 http 连接是否成功

//  pagination.test.js
const Nightmare = require('nightmare');
const chai = require('chai');

chai.should();

//初始化Nightmare对象
const nightmare = new Nightmare({
  show: true, //是否显示图形化界面
  openDevTools: {
    //配置此项后可显示开发者工具,不配置即不显示
    mode: 'detach',
  },
});

describe('pagination test', function () {
  // 可以关闭超时事件或者直接设置超时时间,默认为两秒关闭
  this.timeout(5000);
  it('http connection', function (done) {
    nightmare
      .goto('http://127.0.0.1:3000/pagination') //打开某网页
      .then(() => {
        done();
      }) //成功后调done执行结果
      .catch((err) => {
        {
          console.warn('加载出错', err);
          done(err);
        }
      }); //出错执行什么
  });
});

测试

# 启动 node server
npm  run demos

# 运行测试
npm test

> csdwheels@1.3.6 test F:\Source code\csdwheels
> mocha "./test/**/*.test.js"

  backtop test
    √ http connection (1045ms)

  calendar test
    √ http connection (1148ms)

  carousel test
    √ http connection (2911ms)

  eventemitter test
    √ http connection (1037ms)

  magnifier test
    √ http connection (3327ms)

  pagination test
    √ http connection (1058ms)


  6 passing (11s)

测试覆盖率 + 持续集成:codecov + travis-ci

在有了上面的单元测试持续集成的底子,我们再来为代码加入测试覆盖率的徽章,它将有效不打折扣的反应测试的实际质量.

Demo: rainbow-utils

package.json中设置好测试用的钩子函数,这次使用的 jest进行单元测试,它比 mocha更为强大.

{
  "test": "jest",
  "coverage": "jest --coverage",
  // 改进
  "upload-coverage": "jest --coverage && codecov -f ./coverage/coverage-final.json"
}
  • npm run test:将反应测试的通过情况
  • npm run coverage:将反应测试覆盖率的实际情况

我们运行后查看本地执行后测试覆盖率的情况:

-------------|---------|----------|---------|---------|------------------------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|------------------------------------
All files    |   61.08 |    42.01 |   57.14 |    62.1 |
 src         |   70.88 |    53.41 |   64.58 |   73.05 |
  core.js    |   74.81 |    51.47 |   70.27 |   76.61 | 94,106,137,154,208,247-290,298-302
  feat.js    |     100 |    85.71 |     100 |     100 | 55,66
  file.js    |       0 |        0 |       0 |       0 | 7-45
  index.js   |       0 |        0 |       0 |       0 |
 src/modules |   49.34 |    29.63 |   48.84 |   49.66 |
  dom.js     |   22.06 |    14.71 |      25 |   21.88 | 23-103,109-110,115-148
  event.js   |     100 |    72.22 |     100 |     100 | 14,29-35,64-74
  fetch.js   |   24.14 |        0 |       0 |   24.14 | 11-57,64,68,72,76,80
  storage.js |   86.67 |       75 |     100 |   85.71 | 18-19
-------------|---------|----------|---------|---------|------------------------------------

Test Suites: 16 passed, 16 total
Tests:       44 passed, 44 total
Snapshots:   0 total
Time:        7.53 s
Ran all test suites

此刻我们的 code 有 61%都有覆盖到测试用例并成功通过,运行完成之后你就会发现你的目录中多了一个 coverage 的目录,打开它,里面有两个关键信息我们会用到一个是 coverage-final.json文件里面是测试的结果文件,另外一个找到lcov-report/src/index.html,然后用浏览器打开。 反应的是与上图一致的可视化测试结果页面.

下一步,我们将测试结果与 codecov.io关联起来,就像 travis-ci一样.

打开 https://codecov.io 使用 github 登录。找到 Add new repository 按钮,然后选择一个我们的 rainbow-utils repository,这时候系统会给你一个 Upload Token,有了这个 Token 你就能证明你是你了,然后就可以在任何地方凭借着这个 Token 往你的 codecov 中上传报告了。

现在我们只要运行:

CODECOV_TOKEN="21639620-4627-42ef-a21f-f270c6358671" npm run upload-coverage

就可以将你的代码报告上传了。

但是!

每次都需要输入这个 Token 是不是有点不爽?我们能不能让我们的好伙伴 Travis 来解决这个问题?答案是可以的,还记得我们的 Travis 配置么? 我们可以在配置中追加 after_script当构建完成后的脚本阶段(最后一个自定义钩子)自动帮我们执行 :

after_script:
  - npm run upload-coverage

但是,token 去哪里了呢? 为什么这里不用写呢?那么它是怎么知道我们的权限的呢?

试想如果我们把 Token 写在了配置里面的话,这就意味着每个人都能看到我们的 token 并且能盗用我们的权限,这样做是十分不安全的,所以 Travis 想到了这一点,给了我们一个添加系统变量的机会。

想要设置当个项目的系统变量,你需要打卡 Travis 网站的单个 repository 实例中 ,找到Setting下的 Environment Variables栏目,把你的 Token 填入, NAME 为 CODECOV_TOKEN,VALUE 为 Token 值,点击最后的 Add添加按钮.

这样,Travis 在每次运行 upload-coverage 的时候,就能自动的填入 Token 了。

大功告成!

构建结果:

NPM version Build Status codecov

参考链接

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant