NutUI 3.0 中单元测试的探索和实践

众所周知,单元测试功能,是组件库开发中必不可少的一部分,负责进行检查和验证,保证了组件的合理性和规范性。本文主要讲的就是单元测试在 NUTUI 组件库 中的探索实践,咱们将从如何编写单元测试、持续集成服务、Coveralls 自动测试代码覆盖率三方面进行阐述。如图所示:css

image

若是你对这些内容感兴趣,就和我一块儿来看一下吧!html

单元测试配置

在进入单元测试配置正文以前,咱们先来了解下面两个问题。vue

  • 单元测试是什么?
  • 为何须要单元测试?

单元测试是什么?

单元测试(unit testing),能够对软件中的最小可测试单元进行检查和验证,是软件开发中重要的一部分。它使得添加新功能和追踪问题更加容易。node

为何须要单元测试?

单元测试在开发的时候颇有用,即能帮助开发者思考如何设计一个组件,也可以重构一个现有组件。每次代码发生变化的时候它们都会被运行。有了单元测试,咱们能够自信的交付本身的代码,而没有任何的后顾之忧。 组件的单元测试有如下等优势:webpack

  • 提供描述组件行为的文档
  • 减小调试时间,节省手动测试的时间
  • 减小研发新特性时产生的 bug,测出功能的隐藏 bug
  • 减小和快速定位 bug
  • 促进重构,保证代码重构的安全性

如何编写单元测试?

咱们既是单元测试的受益者,同时也是开发者,接下来咱们进入正题,来聊一聊,如何在 vue 组件库中,加入单元测试。 单元测试用到的工具大体分为三部分:测试框架、测试运行器、断言库。web

测试框架

由于咱们是 vue 组件库,因此使用 Vue Test Utils 做为测试框架,它是 Vue 组件单元测试的官方库,有详细的指引和自定义设置用于测试,文档清晰,容易上手。npm

咱们将其做为开发依赖安装在项目中:json

npm install --save-dev @vue/test-utils浏览器

它依赖浏览器环境,能够运行在真实的浏览器或是 Node 虚拟浏览器中,由于在不一样的平台上启动真实的浏览器是比较复杂的。因此咱们让其运行在 Node 虚拟浏览器中,这就须要借助 JSDOM 帮助咱们让它在 Node 虚拟浏览器环境运行测试。sass

咱们把 JSDOM 作为开发依赖安装:

npm install --save-dev jsdom jsdom-global

而后在项目根目录建立 /test 的空文件夹,建立 setup.js 文件

require('jsdom-global')()

在测试入口处使用 jsdom-global 手动设置 JSDOM 便可。这样每次运行单元测试都会执行 setup.js 文件,从而引入 JSDOM。

测试运行器

测试运行器(test runner)就是运行测试的程序。测试运行器不少,Vue Test Utils 基本上能够支持主流 JavaScript 测试运行器。比较好的是,Vue Test Utils 帮咱们筛选了一遍,推荐了两个测试运行器给咱们,咱们从中选择一个便可,

  • Jest: 功能最全的测试运行器。它所需的配置是最少的,默认安装了 JSDOM,内置断言且命令行的用户体验很是好。
  • mocha-webpack: webpack + Mocha 的包裹器,包含顺畅的接口和侦听模式。

测试单文件组件的策略是经过 webpack 编译全部的测试文件,而后在测试运行器中运行,使用 mocha-webpack 好处在于咱们可以经过 webpack + vue-loader 获得完整的单文件组件支持,咱们没必要对源代码作任何妥协,虽然 Jest 也提供了vue-jest 预处理器来处理最多见的单文件组件,但仍不是 vue-loader 100% 的功能。因此咱们选用了 mocha-webpack。

经过 npm 安装 mocha、mocha-webpack:

npm install --save-dev mocha mocha-webpack

须要注意的是,mocha-webpack 依赖 webpack 和 mocha,而且对版本有要求 webpack 版本须要4.x.x,mocha 版本为4.x.x & 5.x.x。

断言库

咱们使用 mocha 进行测试的时候,须要结合断言库去使用,Mocha 不像 Jest 框架同样有内置的断言库。它容许咱们本身选择合适的断言库。expect 是一款断言库,它极简的 BDD 风格,获得不少测试框架的承认,这些测试框架内置的也是 expect 断言库,所以咱们本次也使用了 expect 断言库。

首先安装开发依赖:

$ npm install --save-dev expect

并在 test/setup.js 中写入:

global.expect = require('expect')

令其全局可用,这样就不须要在每一个测试文件里导入它了。

安装好了各类开发依赖以后,在根目录 package.json 文件中定义定义一个 npm 脚本 test 命令

{
    ...
    "scripts": {
        ...
        "test": "cross-env NODE_ENV=test mocha-webpack --webpack-config dist_cli/webpack/test.config.js --require dist_cli/test/setup.js src/packages/*/__test__/**.spec.js"
    },
    ...
}
复制代码

值得注意的是,若是项目没有安装 cross-env,须要先安装一下,它用于跨平台设置环境变量。 简单来了解一下 test 命令参数的含义:

  • --webpack-config:指定了该测试使用的 webpack 配置文件。
  • --require:确保了文件 test/setup.js 会在任何测试以前运行,这样咱们能够在该文件中设置测试所需的全局环境。
  • 最后一个参数 src/ 目录:是该测试包所涵盖的全部测试文件的集合。

准备好以上环境,在命令行执行 npm test 就能够执行测试。 效果以下:

image

最基本的单元测试已经配置完成,但咱们的工做尚未结束,继续往下看

增长单元测试代码覆盖率

覆盖率既是度量测试完整性的一个手段,也是测试有效性的一个度量。测试覆盖是对测试彻底程度的评测。 Mocha 是 JavaScript 项目的测试工具,Istanbul 是 JS 测试覆盖率报告的生成工具。咱们利用两者测试代码并生成代码库的测试覆盖率报告。

nyc 是 Istanbul 的命令行接口,咱们将其做为开发依赖安装在项目中:

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

而后在上面的 npm 脚本增长 nyc

{
    ...
    "scripts": {
        ...
        "test": "cross-env NODE_ENV=test nyc mocha-webpack --webpack-config dist_cli/webpack/test.config.js --require dist_cli/test/setup.js src/packages/*/__test__/**.spec.js"
    },
    ...
}
复制代码

同时须要在的 package.json 中配置 nyc:

{
    ...
    "nyc": {
    "include": [
        "../../../src/packages/**/*.vue"
    ],
    "reporter": [
        "lcov",
        "text"
    ],
    "instrument": false,
    "sourceMap": false
    },
    ...
}
复制代码

介绍一下参数的含义:

  • include: 测试文件路径
  • reporter: 输出 lcov (lcov.info + html 报告)和文本形式的覆盖率报告
  • instrument 和 sourceMap: 设置为 false,禁用 nyc 去 instrumenting 和 sourceMap 你的代码,后续咱们指定 loader 去完成。

这时咱们运行 npm test 命令, 就能够得到代码库的测试覆盖率报告。

image

如图所示,出现了多个 Unknow,显然还有一些问题,须要安装一下 istanbul-instrumenter-loader,而后把 loader 加到咱们的 test.config.ts 的配置文件中。再把生成环境的配置文件 package.conf.ts 导入 merge 进去。

import { ROOT_PACKAGE_PATH } from '../common/dic';
import { packageConfig } from './package.config';
import merge from 'webpack-merge';
module.exports =  merge(packageConfig(false), {
    module: {
        rules: [
            {
                test: /\.(js|ts)/,
                use: {
                    loader: 'istanbul-instrumenter-loader',
                    options: { esModules: true }
                },
                include: [ROOT_PACKAGE_PATH('src/packages')]
            },
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader', 
                ]
            },
            {
                test: /\.scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    {
                        loader: 'sass-loader',
                        options: { 
                            prependData: `@import "@/styles/index.scss"; `
                        }
                    }
                ]
            }
        ],
    },
    devtool: 'inline-cheap-module-source-map',
    externals: [require('webpack-node-externals')()] // 忽略node_modules文件夹中的全部模块
});

复制代码

至此,整个项目的代码覆盖率统计配置基本完成,值得注意的是 istanbul-instrumenter-loader 须要放在最上面,保证它最后执行,这时咱们在终端执行 npm test 会显示测试覆盖率结果。

image

同时,在项目根目录,会自动建立 coverage 文件夹,里面有生成测试覆盖率报告文件 (Icov.info+html)。

image

接下来咱们还要介绍一下持续集成服务

持续集成服务

持续集成指的是只要代码有变动,就自动运行构建和测试,反馈运行结果。确保符合预期以后,再将新代码集成到主干, 有助于提升项目质量。经过持续集成能够自动编译、打包、签名项目,配合单元测试能够实现持续集成+自动化测试。咱们的项目维护在 Github 上。Github 有个好朋友 Travis CI,是一个在线托管的分布式持续集成服务,咱们能够用它来构建并测试托管在 Github 上的软件项目。你能够轻松用 Travis CI 同步你的 Github 项目,而且你能够在几分钟内就能测试你的项目。

值得注意的是,Travis CI 分为免费和自费的,对于咱们 Github 上的开源项目,直接能够用免费的,访问 免费版 便可。

下面来看一下怎么让本身的 Github 项目用 Travis CI 测试。

首先,访问 Travis CI 并使用项目的 Github 帐户登陆。 登陆以后,表明你的 Github 对 Travis 进行了受权,Travis 能够访问你的 Github 上全部代码仓库。选择须要 Travis CI 帮你持续集成的仓库,点击右侧的激活开关便可。这样Travis CI 就会帮你监听这个仓库的全部变化自动构建,完成预约的操做。

接下来,咱们须要在咱们代码仓库的根目录添加一个 .travis.yml 文件,来告诉 Travis CI 定义预约的命令,它会告诉 Travis CI 作什么,怎么作。配置以下内容,可按需更改:

sudo: required
language: node_js
node_js:
- '8'
script:
- npm test
- npm run coveralls
复制代码

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

  • sudo: required 表示须要 sudo 权限。
  • language: 指定了项目的语言,node_js 指定运行环境为 Node 。
  • node_js: 指定 Node 版本,可指定多个。
  • install: 安装依赖,用来指定安装脚本,的默认脚本是 npm install
  • script: 用来指定构建或测试脚本, 默认脚本 npm test。

还有不少参数,我就不一一列举了,具体能够查看官网配置

完成上面的操做后,push 这个这个文件到你的 Github 仓库。以后每次往 Github 的该仓库 push 代码,Travis 就会去找这个文件,执行配置好的预约义指令了。

image

这里能够看到运行结果,可点击查看构建过程的详细信息。如运行有误,会有以下提示,咱们要知道,script构建阶段只要有一个失败,状态就会显示失败。

image

以上就是简单的 Travis CI 和 Github 项目的关联过程。

Travis CI 确实很给力,若是想在 Github 项目中直接看到 CI 结果徽标,只须要点击该图标,选择 markdown,而后将 result 文本框内容复制到 Github 上的 README.md 文件中便可。

image

Coveralls自动测试代码覆盖率

最后,咱们想要生成一份代码覆盖率的报告,这里须要使用 Coveralls。Coveralls 支持 Github 上的项目,也能够与 Travis CI集成。 访问 coveralls.io/,使用 Github 帐号登陆,以后点击上面的 Add Repo,接着将按钮置为 ON 状态

image

点击右边 detail,点击 detials 进入详细配置页面,页面右侧,获取该项目的 token,根据本身的环境类型编辑相应配置文件 在根目录下添加 .coverall.yml 文件,并添加下面内容:

service_name: travis-ci
repo_token: bOzghLfr6hi9x**************56vdl1YG
复制代码

给 Coveralls 上传的测试报告须要有统一的 lcov 格式,上文中咱们有对 nyc 进行配置,在根目录 coverage 文件夹中生成 lcov.info 在 package.json 文件的 scripts 字段添加下面这行命令

"coveralls": "cat ./coverage/lcov.info | coveralls",
复制代码

push 代码到Github仓库。 一样,咱们能够获取一个测试覆盖率的徽标,进入刚才的详情配置页。点击按钮复制 markdown 内容到 Github 上的 README.md 文件中便可。

image

总结

单元测试功能已集成在由咱们团队开发的 NUTUI 组件库 上面进行实践,NutUI 是一套京东风格的移动端Vue组件库,开发和服务于移动 Web 界面的企业级前中后台产品。经过 NutUI,能够快速搭建出风格统一的页面,提高开发效率。目前已有近 50 个组件,这些组件被普遍使用于京东的各个移动端业务中。后期咱们会对整个 NutUI 系统架构进行革新,将整个组件库构建工具抽离出,采用 WebPack Node API构建,对编译作出更细粒度的控制,同时加大对编译配置的优化调整,大幅提升性能和减小打包文件体积,提供独立构建的 NutUI-CLI。单元测试的功能也保证了组件的规范化,减小错误。欢迎各位使用,如在使用中有任何问题,咱们也会及时反馈跟进。流年笑掷,将来可期~

image

最后,以上是 NUTUI 组件库 官方网址,欢迎扫码体验~