前端单元测试配置-Mocha和Jest结合Vue&React&Angular

结合现有项目,将使用到的前端框架(Vue、Angular、React)进行单元测试配置,整理配置文档

Vue

vue-cli在创建项目时可选择单元测试框架(Mocha 或 Jest),此处包含两种测试框架配置

Vue & Mocha

项目创建

项目创建时选择 Mocha 的Unit Testing

vue create vue-mocha

# 选择手动配置
? Please pick a preset: 
  zcloud (router, vuex, less, babel, eslint, unit-jest) 
  default (babel, eslint) 
❯ Manually select features 

? Please pick a preset: Manually select features
? Check the features needed for your project: 
 ◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◉ Router
 ◉ Vuex
 ◉ CSS Pre-processors
 ◉ Linter / Formatter
 ◉ Unit Testing
❯◉ E2E Testing

# 单元测试 mocha + chai
 ? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors, Linter, Unit, E2E
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Pick a unit testing solution: (Use arrow keys)
❯ Mocha + Chai 
  Jest 

添加测试报告mochawesome

现有架构测试报告都是在命令行展示,现在我们要将结果输出并展示在页面上

首先在项目中安装 mochawesome:

npm install --save-dev mochawesome

然后在 package.json 中修改 test:unit 命令如下:

"test:unit": "vue-cli-service test:unit --reporter=mochawesome"

添加测试覆盖率nyc

首先,在项目中安装所需依赖:

npm i --save-dev babel-plugin-istanbul istanbul-instrumenter-loader nyc

接着在项目根目录下新建 nyc.config.js 文件,其内容如下:

// 探索istanbul/nyc代码覆盖工具的原理 https://zhuanlan.zhihu.com/p/88524418
module.exports = {
  'check-coverage': true,
  'per-file': true,
  'lines': 0,
  'statements': 0,
  'functions': 0,
  'branches': 0,
  'include': [
    'src/**/*.js',
    'src/**/*.vue'
  ],
  'exclude': [
    'src/abandon-ui/**',
    'src/*.js',
    '**/*.spec.js'
  ],
  'reporter': [
    'lcov',
    'text',
    'text-summary'
  ],
  'extension': [
    '.js',
    '.vue'
  ],
  'cache': true,
  'all': true
}

完成后,在项目根目录新建 vue.config.js, 其内容如下:

const path = require('path')
const testMode = process.env.NODE_ENV === 'test'

module.exports = {
  lintOnSave: false,
  productionSourceMap: false,

  chainWebpack: config => {

    if (testMode) {
      config.merge({
        devtool: 'eval'
      })
      config.module
        .rule('istanbul')
        .test(/\.(js|vue)$/)
        .include
        .add(path.resolve(__dirname, '/src'))
        .end()
        .use('istanbul-instrumenter-loader')
        .loader('istanbul-instrumenter-loader')
        .options({ esModules: true })
        .before('babel-loader')
    }
  }
}

完成后,打开项目根目录下的 babel.config.js 文件,修改如下:

const testMode = process.env.NODE_ENV === 'test'

module.exports = {
  presets: ['@vue/cli-plugin-babel/preset']
  presets: ['@vue/cli-plugin-babel/preset'],
  plugins: testMode ? ['babel-plugin-istanbul'] : []
}

最后,打开 package.json 文件,修改 test:unit 命令:

"test:unit": "nyc vue-cli-service test:unit --reporter=mochawesome"

单元测试文件书写位置调整

如果我们的项目是一个去中心化的架构,我们可能希望我们的单元测试文件位于我们的组件旁边,而不是都写在 tests/unit/ 目录下。

在 tests/unit 目录下新建 index.spec.js 文件,其内容如下:

// require all src files that ends with .spec.js
const srcContext = require.context('../../src/', true, /\.spec.js$/)
srcContext.keys().forEach(srcContext)

修改.eslintrc.js 配置

overrides: [
  {
    files: [
      '**/__tests__/*.{j,t}s?(x)',
      '**/tests/unit/**/*.spec.{j,t}s?(x)',
      '**/src/**/*.spec.{j,t}s?(x)'
    ],
    env: {
      mocha: true
    }
  }
]

至此,Vue+Mocha单元测试基本配置完成,执行test:unit 命令后,可在项目中查看测试效果

Vue & jest

项目创建

项目创建时选择 Jest 的Unit Testing,具体操作可查看Mocha的创建

添加测试报告

安装依赖:

npm i --save-dev jest-html-reporter

配置jest.config.js

jest集成了测试所需的功能,包括断言,覆盖率,以及测试报告等,测试配置文件如下:

module.exports = {
  preset: '@vue/cli-plugin-unit-jest',
  moduleFileExtensions: [
    'js',
    // 告诉 Jest 处理 `*.vue` 文件
    'vue'
  ],
  moduleNameMapper: {
    // 支持源代码中相同的 `@` -> `src` 别名
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  transform: {
    // 用 `babel-jest` 处理 `*.js` 文件
    '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
    // 用 `vue-jest` 处理 `*.vue` 文件
    '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
  },
  testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)', '**/src/**/*.spec.{j,t}s?(x)'],
  snapshotSerializers: [
    // 快照的序列化工具
    '<rootDir>/node_modules/jest-serializer-vue'
  ],
  displayName: {
    name: 'CLIENT',
    color: 'blue'
  },
  // 成多种格式的测试覆盖率报告  可选
  collectCoverage: true,
  collectCoverageFrom: ['**/*.{js,vue}', '!**/node_modules/**', '!**/dist/**', '!**/coverage/**'],
  // report配置
  reporters: [
    'default',
    [
      './node_modules/jest-html-reporter',
      {
        pageTitle: 'test report',
        outputPath: 'testReport/JesttestReport.html',
        includeFailureMsg: true
      }
    ]
  ]
}

至此,Vue+Jest单元测试基本配置完成,执行test:unit 命令后,可在项目中查看测试效果。

Jest相对Mocha而言,配置简单许多,开箱即用,考虑到不同的库需要学习不同的API,踩坑的风险也随之提升,后续框架均只采用Jest进行配置。

Angular

Angular脚手架在创建项目时自动配置单元测试,采用Karma和Jasmine,引入Jest需要迁移

angular & Jest

配置时默认使用angular脚手架创建的项目

迁移

第一步,下载相关的依赖包:

npm i --save-dev jest jest-preset-angular @types/jest jest-html-reporter

第二步,在pakage.json中对Jest进行配置,也可直接配置jest.config.js

"jest": {
  "preset": "jest-preset-angular",
  "setupFilesAfterEnv": ["<rootDir>/src/setupJest.ts"]
}

第三步,在src目录下创建上一步中设置的 setup 文件setupJest.ts

import 'jest-preset-angular'; // jest 对于 angular 的预配置

接下来,我们就可以在 package.json 的 script 中配置 test 的命令了:

"test": "jest"

此时,在命令行中运行测试命令,就应该能够顺利把测试跑起来并通过了。如果没有通过,可能是因为我们在src/tsconfig.spec.json中的 file 配置中有test.ts的配置,这是 Karma 的 setup 文件,删掉这行配置并删除对应的文件,(src/tsconfig.app.json中出现的test.ts也可一并删除),重新执行npm run test即可

删除 Karma 相关代码

  • 删除相关依赖包(@types/jasmine @types/jasminewd2 jasmine-core jasmine-spec-reporter 因为在 e2e 测试中有使用所以不能删除):
    npm uninstall karma karma-chrome-launcher karma-coverage-istanbul-reporter karma-jasmine karma-jasmine-html-reporter
    
  • 删除文件src/karma.config.js
  • 删除 angular.json中test的配置
  • src/tsconfig.spec.jsoncompilerOptions.type的配置移除 jasmine, 加上 jest。

至此,Jest测试环境就算顺利搭建好了

React

Create React App创建后react项目后,是默认集成Jest的,所以我们只需完善配置即可

浅层渲染

如果你希望测试独立于它们渲染的子组件的组件,我们建议使用 Enzyme 的 shallow() 渲染API。

第一步,安装enzyme依赖

npm install --save-dev enzyme enzyme-adapter-react-16 react-test-renderer

第二步,修改src/setupTests.js

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

// 添加在测试中模拟的浏览器API
const localStorageMock = {
  getItem: jest.fn(),
  setItem: jest.fn(),
  removeItem: jest.fn(),
  clear: jest.fn(),
};
global.localStorage = localStorageMock;

覆盖率报告

Jest 有一个集成的覆盖率报告器,可以很好地与 ES6 配合使用,无需配置。运行 npm test -- --coverage

进阶学习文档

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348