Vue组件库工程探索与实践之单元测试

本文是《Vue组件库工程探索与实践》系列文章第三篇,聊聊组件库单元测试和持续集成功能的实现。javascript

单元测试是软件工程领域的一个重要概念,指对软件中的最小可测试单元进行检查和验证,它是代码正确性验证的最重要的工具。单元测试也是组件库实现自动化测试与集成的基础。html

单元测试会封闭执行最小化单元的代码,使添加新功能和追踪问题更容易。对代码进行单元测试有不少好处:前端

  • 可节省手动测试的时间
  • 有助于减小开发新特性时产生的反作用
  • 有利于改进设计和重构
  • 有助于提高大团队中复杂基础代码的可维护性

前端小伙伴们对单元测试多多少少都有必定的了解,但真正有实践经验的并不太多。我想可能与业务项目工期紧张、需求变化比变脸还快、对单元测试的意义认识不够等方面因素有关。固然,也不是全部项目都须要单元测试。vue

程序猿是这么死的

不过,随着时间的推移,阅历的累积,或许有一天你会真正意识到它的价值,就像李宗盛的歌。java

少年不听李宗盛,听懂已经是不惑年

对于组件库这种可复用的公共代码来讲,其对可靠性、可维护性的要求较普通业务类项目代码更高,引入单元测试是十分必要的。这里分享一下咱们在Vue组件库的单元测试方面的探索与实践。node

限于篇幅,本文重点谈Vue组件库的单元测试功能及实现,不会过多介绍单元测试基础知识。webpack

首先咱们须要明确,Vue组件库的单元测试,主要仍是针对其中的Vue组件。git

Vue Test Utils

为了方便对 Vue 单文件组件进行测试,Vue.js 官方提供了测试工具库 Vue Test Utils,它提供了一系列方法用于测试Vue组件。github

Vue Test Utils 经过把组件隔离挂载,模拟必要的输入 (prop、注入和用户事件) 和对输出 (渲染结果、触发的自定义事件) 的断言来测试 Vue 组件。被挂载的组件会返回到一个包裹器内,而包裹器会暴露不少封装、遍历和查询其内部的 Vue 组件实例的便捷的方法。此外,Vue Test Utils 还提供了模拟用户交互的相关方法。web

可见,Vue Test Utils 是 Vue 组件单元测试的必备工具,须要安装。

$ npm install --save-dev @vue/test-utils
复制代码

测试运行器

测试运行器 test runner 即运行测试的程序,也就是咱们一般所说的测试框架。Vue Test Utils 是与测试运行器无关的,主流的测试运行器都支持。

官方推荐两个测试运行器:Jestmocha-webpack

Jest 是 Facebook 开源的一套 JavaScript 测试框架, 它自动集成了断言、JSDOM、覆盖率报告等几乎全部测试工具,功能至关强大。它所需的配置是最少的,不过须要一个可以将Vue单文件组件导入到测试中的预处理器。Vue.js官方提供了 vue-jest 预处理器来处理最多见的单文件组件特性,但仍不是 vue-loader 所有的功能。

mocha-webpack 是知名前端测试框架 Mochawebpack 的一个包裹器。下面这行命令就是它的大体工做原理:先对文件进行 webpack 编译,再用 Mocha 对编译后的文件进行测试。固然,其中还包含不少优化工做。

$ webpack test.js output.js && mocha output.js
复制代码

所以咱们可以经过 webpack + vue-loader 获得完整的Vue单文件组件支持。mocha-webpack 的配置比 Jest 复杂不少。

mocha-webpack2.0.0-beta.0版支持 webpack 4

为了得到完整的Vue单文件组件支持,咱们在 NutUI 2.0 项目中选择了 mocha-webpack 测试运行器。

浏览器环境

Vue.js 官方的测试工具库 Vue Test Utils 依赖浏览器环境,而启动真实的浏览器是很是复杂的,由于涉及到不一样的平台和版本,兼容性与稳定性不易处理。从另外一个角度看,自动测试一般不须要浏览器的用户界面,使用无头浏览器(headless browsers)就能知足需求。

无头浏览器是一种没有图形用户界面的网页浏览器,能够在相似于流行的 Web 浏览器的环境中提供对网页的自动控制,经过命令行界面或使用网络通讯来执行。它们能以与浏览器相同的方式渲染和理解 HTML,包括页面布局、颜色、字体选择、JavaScript 与 Ajax 的执行等等。一般用于 Web 的自动化测试等场景。

当下比较流行的无头浏览器有 JSDOMPuppeteerSelenium 等等,咱们的项目中选择了 JSDOM 在 Node 虚拟浏览器环境运行测试。

$ npm install --save-dev jsdom jsdom-global
复制代码
// 在测试的配置文件中
require('jsdom-global')()
复制代码

断言库

“断言”就是判断代码的实际执行结果与预期结果是否一致,若是不一致就抛出一个错误。全部的测试用例都应该包含至少一条断言。它是编写测试用例的关键。

好比下面这条断言,它的含义是调用 add(1,1) 的结果应该等于2。

expect(add(1,1)).to.be.equal(2);
复制代码

断言功能由断言库来实现。Jest 框架内置了断言库 expect,而 Mocha不包含断言库。因此选择使用 Mocha框架的时候,咱们还须要额外安装一个断言库。

expect 断言风格(如上面的栗子)很接近天然语言,NutUI 2.x 项目的断言库选用的也是 expect。除了 expect,流行的断言库还有 chaiassertshould 等。

$ npm install --save-dev expect
复制代码

在测试的配置文件中引入 expect,并把它挂到全局对象上,固然也能够在每一个测试文件中分别导入。

global.expect = require('expect')
复制代码

测试环境配置

在肯定了测试运行器使用 mocha-webpack,浏览器环境使用 JSDOM,断言库使用 expect 以后,咱们就能够开始在组件库脚手架中进行测试工具的安装和配置了。

首先安装测试所需依赖。

$ npm install --save-dev @vue/test-utils mocha mocha-webpack@2.0.0-beta.0 expect jsdom jsdom-global
复制代码

而后,在项目根目录下新建 test/setup.js 目录及文件,用来设置测试所需的全局环境。咱们在该文件中引入 JSDOMexpect

test/setup.js

require('jsdom-global')();
global.expect = require('expect');
复制代码

接着,在 package.json文件中新增一个测试脚本。

package.json

{
  "scripts": {
    "test": "mocha-webpack --webpack-config webpack.test.conf.js --require test/setup.js src/packages/*/__test__/**.spec.js"
  }
}
复制代码
  • --webpack-config 指定了测试使用的 webpack 配置文件 webpack.test.conf.js。该文件与生产环境配置文件有些差别,下文会细说。
  • --require 标识确保文件 test/setup.js 在测试以前运行,所以咱们能够在该文件中设置测试所需的全局环境。
  • 最后一个参数是该测试包所涵盖的全部测试文件的聚合。

测试覆盖率

测试覆盖率是对测试彻底程度的度量,是由测试需求、测试用例的覆盖或已执行代码的覆盖表示的。

一个统计 JavaScript 单元测试覆盖率的知名工具是 istanbul,它以土耳其最大城市伊斯坦布尔命名,由于土耳其地毯世界闻名,而地毯是用来覆盖的。

伊斯坦布尔

咱们来看下在 mocha-webpack框架下,如何使用 istanbul统计测试覆盖率。

首先,咱们须要安装 nycistanbul-instrumenter-loader:

$ npm install --save-dev nyc istanbul-instrumenter-loader
复制代码
  • nyc: istanbul的命令行工具。
  • istanbul-instrumenter-loader: 使用钩子(hooks)包装代码以在代码执行时跟踪覆盖率的loader。

接下来,修改 package.json文件 scripts字段下 test脚本,同时增长 nyc的相关配置项:

package.json

{
  ...
  "scripts": {
    "test": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=text mocha-webpack --webpack-config build/webpack.test.conf.js --require test/setup.js src/packages/*/__test__/**.spec.js"
    ...
  },
  "nyc": {
    "include": [
      "src/packages/**/*.vue"
    ],
    "instrument": false,
    "sourceMap": false
   },
   ...
}
复制代码

test脚本中 --reporter=lcov --reporter=text是配置 nyc同时输出lcov (lcov.info + html)和文本形式的覆盖率报告。

scripts字段下方 nyc的配置项中:

  • include 用来指定被测试源文件的位置。
  • 禁用 nycinstrumentsourceMap 选项,由于这些工做应该由 loader 负责。

接下来须要把 istanbul-instrumenter-loader加到 webpack 的配置文件中。上文提到,咱们给单元测试工做指定的 webpack 配置文件是 webpack.test.conf.js,它与生产环境的配置文件有些差别,咱们先把生成环境的配置文件 webpack.prod.conf.js 导入,再把这些差别 merge 进去。

webpack.test.conf.js

const path = require('path');
const prodConf = require('./webpack.prod.conf.js');
const merge = require('webpack-merge');

module.exports = merge(prodConf, {
    module: {
        rules: [
            {
                test: /\.(js|ts)/,
                use: {
                    loader: 'istanbul-instrumenter-loader',
                    options: { esModules: true }
                },
                include: path.resolve(__dirname, '../src/packages/')
            },
        ],
    },
    devtool: 'inline-cheap-module-source-map',
    externals: [require('webpack-node-externals')()]
});
复制代码
  • 咱们给 js/ts 类型的文件新增了 loader: istanbul-instrumenter-loader,要注意的是,该 loader 必须放在数组的第一个,以确保它最后一个应用。
  • sourceMapmocha-webpack中必须经过内联方式获取,因此 devtool 选项推荐的值为 inline-cheap-module-source-map
  • 为了提高测试的启动速度,咱们能够经过 webpack-node-externals 外置全部的 NPM 依赖。

至此,整个项目的自动化测试功能及覆盖率统计配置基本完成,咱们在 src/packages/ 目录下的每一个组件的目录下新建一个__test__目录,该目录下的单元测试文件以 .spec.js 做为扩展名。

目录结构

准备好组件的单元测试文件以后,在终端执行 npm test 便可启动测试。测试的过程当中,终端会展现每一个测试用例的测试结果。

单元测试

测试结束以后,终端会以文本形式展现出本次测试的覆盖率报告,同时 /coverage目录下也会生成测试覆盖率报告文件(Icov.info+html)。

测试覆盖率

持续集成

持续集成(Continuous Integration, CI)是一种软件开发实践,即每次代码的集成都经过自动化的构建(包括编译/发布/自动化测试)来验证,从而尽早的发现集成错误。也就是说,只要代码有变动,就自动运行构建和测试,确保符合预期后,再将新代码集成到主干。它的核心措施是,在代码集成到主干以前,必须经过自动化测试。

持续集成的意义在于:

  • 每次代码提交都进行自动构建和测试,有助于尽早的发现问题和解决问题,减小风险。
  • 自动化的构建和测试能够减小人重复的工做,节约时间,下降成本。
  • 有助于提高项目质量。

Travis CI 是在线托管的 CI 服务,使用 Travis 来进行持续集成。它对于开源项目是免费的,支持绑定 Github 上面的项目。只要有新的代码,就会自动抓取。而后,提供一个运行环境,自动进行构建和测试,还支持部署到服务器。

咱们来看下 Travis CI的基本用法。

首先,访问 Travis CI 官网并使用 Github 帐户登陆。 而后,点击其网站右上角的我的头像,网页会列出 Github 上咱们和咱们所在的组织的全部代码仓库。打开须要进行 CI 的仓库右侧的开关便可。

激活仓库

接下来,咱们须要在这个代码仓库的根目录放置一个名为 .travis.ymlTravis CI 配置文件。NutUI 2.x项目的配置文件内容以下:

sudo: required
language: node_js
node_js:
 - '8'
script:
 - npm test
 - npm run coveralls
复制代码
  • sudo: required 表示须要 sudo 权限。
  • language: node_js 指定运行环境为 Node 。
  • node_js 字段用来指定 Node 版本。
  • script 字段用来指定构建或者测试的脚本。

Travis 的运行流程包含两个阶段:install(安装依赖)和 script(执行脚本)。对于 Node 项目来讲,installscript阶段都有默认脚本,如不须要修改,能够省略不写。

  • install的默认脚本是:npm install
  • script的默认脚本是:npm test

Travis CI 还支持自动部署,但不是必须的。咱们的组件库没有用到,这里很少说。

配置完成以后,每次往 Github 的该仓库 push 代码,都会触发 CI。登陆 Travis CI网站能够看到结果。

NutUI某次自动构建结果

咱们还能够从 Travis CI 网站获取一个关联该仓库 CI 结果的徽标,放在咱们项目的 README.md 文件中,这样即使不登陆 Travis CI 网站,咱们也能够经过该徽标知晓 CI 结果了。

CI结果徽标

测试覆盖率上报

查看 NutUIREADME.md文件,会发现除了上文提到的CI结果徽标,还有一个展现测试覆盖率的徽标。

这个徽标是经过 coveralls.io获取的。 coveralls.io 提供测试覆盖率的追踪服务,咱们能够把测试覆盖率报告上报给 coveralls.io ,它会基于接收到的数据生成一个测试覆盖率徽标。

coveralls.io 支持 Github 上的项目,也能够与 Travis CI集成。关于它的具体使用,限于篇幅,这里就不展开了,有兴趣的小伙伴能够阅读其官方文档。


好了,这篇文章先聊到这里。若是对具体实现细节感兴趣,能够查看 NutUI 2.x 项目的源码,也欢迎各位老铁Star,赠人Star,手有余香~

连接

相关文章
相关标签/搜索