查看原文
其他

【第1884期】NutUI3.0 中单元测试的探索和实践

宋成林 前端早读课 2020-03-14

前言

今日早读文章由京东用户体验设计部@宋成林投稿分享。

京东用户体验设计部-前端开发部现有前端开发人员 50 左右,主要为京东零售集团、京东健康提供 WEB 前端开发、APP RN开发,小程序开发、小游戏开发、H5开发等能力支持。

正文从这开始~~

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

如果你对这些内容感兴趣,就和我一起来看一下吧!

单元测试配置

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

  • 单元测试是什么?

  • 为什么需要单元测试?

单元测试是什么?

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

为什么需要单元测试?

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

组件的单元测试有以下等优点:

  • 提供描述组件行为的文档

  • 减少调试时间,节省手动测试的时间

  • 减少研发新特性时产生的 bug,测出功能的隐藏 bug

  • 减少和快速定位 bug

  • 促进重构,保证代码重构的安全性

如何编写单元测试?

我们既是单元测试的受益者,同时也是开发者,接下来我们进入正题,来聊一聊,如何在 vue 组件库中,加入单元测试。

单元测试用到的工具大致分为三部分:测试框架、测试运行器、断言库。

测试框架

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

我们将其作为开发依赖安装在项目中:

  1. npm install --save-dev @vue/test-utils

它依赖浏览器环境,可以运行在真实的浏览器或是 Node 虚拟浏览器中,因为在不同的平台上启动真实的浏览器是比较复杂的。所以我们让其运行在 Node 虚拟浏览器中,这就需要借助 JSDOM 帮助我们让它在 Node 虚拟浏览器环境运行测试。

我们把 JSDOM 做为开发依赖安装:

  1. npm install --save-dev jsdom jsdom-global

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

  1. 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:

  1. 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 断言库。

首先安装开发依赖:

  1. $ npm install --save-dev expect

并在 test/setup.js 中写入:

  1. global.expect = require('expect')

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

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

  1. {

  2. ...

  3. "scripts": {

  4. ...

  5. "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"

  6. },

  7. ...

  8. }

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

  • --webpack-config:指定了该测试使用的 webpack 配置文件。

  • --require:确保了文件 test/setup.js 会在任何测试之前运行,这样我们可以在该文件中设置测试所需的全局环境。

  • 最后一个参数 src/ 目录:是该测试包所涵盖的所有测试文件的集合。

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

最基本的单元测试已经配置完成,但我们的工作还没有结束,继续往下看

增加单元测试代码覆盖率

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

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

  1. $ npm install --save-dev nyc

然后在上面的 npm 脚本增加 nyc

  1. {

  2. ...

  3. "scripts": {

  4. ...

  5. "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"

  6. },

  7. ...

  8. }

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

  1. {

  2. ...

  3. "nyc": {

  4. "include": [

  5. "../../../src/packages/**/*.vue"

  6. ],

  7. "reporter": [

  8. "lcov",

  9. "text"

  10. ],

  11. "instrument": false,

  12. "sourceMap": false

  13. },

  14. ...

  15. }

介绍一下参数的含义:

  • include: 测试文件路径

  • reporter: 输出 lcov (lcov.info + html 报告)和文本形式的覆盖率报告

  • instrument 和 sourceMap: 设置为 false,禁用 nyc 去 instrumenting 和 sourceMap 你的代码,后续我们指定 loader 去完成。

这时我们运行 npm test 命令, 就可以获得代码库的测试覆盖率报告。

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

  1. import { ROOT_PACKAGE_PATH } from '../common/dic';

  2. import { packageConfig } from './package.config';

  3. import merge from 'webpack-merge';

  4. module.exports = merge(packageConfig(false), {

  5. module: {

  6. rules: [

  7. {

  8. test: /\.(js|ts)/,

  9. use: {

  10. loader: 'istanbul-instrumenter-loader',

  11. options: { esModules: true }

  12. },

  13. include: [ROOT_PACKAGE_PATH('src/packages')]

  14. },

  15. {

  16. test: /\.css$/,

  17. use: [

  18. 'style-loader',

  19. 'css-loader',

  20. ]

  21. },

  22. {

  23. test: /\.scss$/,

  24. use: [

  25. 'style-loader',

  26. 'css-loader',

  27. {

  28. loader: 'sass-loader',

  29. options: {

  30. prependData: `@import "@/styles/index.scss"; `

  31. }

  32. }

  33. ]

  34. }

  35. ],

  36. },

  37. devtool: 'inline-cheap-module-source-map',

  38. externals: [require('webpack-node-externals')()] // 忽略node_modules文件夹中的所有模块

  39. });

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

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

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

持续集成服务

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

值得注意的是,Travis CI 分为免费和自费的,对于我们 Github 上的开源项目,直接可以用免费的,访问免费版[2] 即可。

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

首先,访问 Travis CI[2] 并使用项目的 Github 账户登录。

登录之后,代表你的 Github 对 Travis 进行了授权,Travis 可以访问你的 Github 上所有代码仓库。选择需要 Travis CI 帮你持续集成的仓库,点击右侧的激活开关即可。这样Travis CI 就会帮你监听这个仓库的所有变化自动构建,完成预定的操作。

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

  1. sudo: required

  2. language: node_js

  3. node_js:

  4. - '8'

  5. script:

  6. - npm test

  7. - 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 就会去找这个文件,执行配置好的预定义指令了。

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

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

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

Coveralls自动测试代码覆盖率

最后,我们想要生成一份代码覆盖率的报告,这里需要使用 Coveralls。Coveralls 支持 Github 上的项目,也可以与 Travis CI集成。访问 Coveralls[3],使用 Github 账号登录,之后点击上面的 Add Repo,接着将按钮置为 ON 状态

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

  1. service_name: travis-ci

  2. repo_token: bOzghLfr6hi9x**************56vdl1YG

给 Coveralls 上传的测试报告需要有统一的 lcov 格式,上文中我们有对 nyc 进行配置,在根目录 coverage 文件夹中生成 lcov.info

在 package.json 文件的 scripts 字段添加下面这行命令

  1. "coveralls": "cat ./coverage/lcov.info | coveralls",

push 代码到Github仓库。

同样,我们可以获取一个测试覆盖率的徽标,进入刚才的详情配置页。点击按钮复制 markdown 内容到 Github 上的 README.md 文件中即可。

总结

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

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

扩展阅读

[1] NUTUI 组件库:http://nutui.jd.com [2] Travis CI:http://travis-ci.org [3] Coveralls网站:https://coveralls.io

@京东用户体验设计部曾分享过


【第1664期】Vue组件库工程探索与实践之单元测试


【第1642期】Vue组件库工程探索与实践之按需加载


【第1585期】Vue组件库工程探索与实践之构建工具


为你推荐


京东开源Vue移动端组件库NutUI2.0发布


【第1827期】小程序跨框架开发的探索与实践

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存