Vue 单元测试(Mocha/Karma/Chai/Vue-Test-Utils/Sinon) 搭建流程

2018/11/19

一. Karma/Mocha 实现 最基本的单元测试

1. 初始化 Vue 项目

首先我们使用

vue init webpack

初始化一个最基本的 Vue 项目, 本着学习目的, 其中 Set up unit tests 选项我们选择 N ,如果为了项目方便, 可选择 Y ,Vue 会帮助搭建一个包含了单元测试模块的项目。

之后我们在项目根目录建立 test/unit 目录, 作为我们单元测试的根目录

2. 安装配置 Karma、Mocha

安装相关的包如下

由于 Vue 项目使用 webpack 搭建, 因此 我们还需要额外安装 karma-webpack

使用 karma-sourcemap-loader 生成 sourcemap

npm i karma mocha karma-mocha karma-webpack karma-sourcemap-loader karma-chrome-launcher -D
npm i karma-cli -g

之后我们初始化一个 karma 的配置文件

karma init

框架选择 mocha、不需要 requireJs、浏览器推荐无头Chrome(ChromeHeadless)、其他选项默认即可

将生成的 karma.conf.js 移动至 test/unit 文件夹下

我们需要关注配置文件中的几个常用配置:

  • browsers 测试浏览器数组
  • frameworks 使用的测试框架
  • reporters 测试结果展示工具
  • files 要引入测试的文件, 包括 被测试的源文件 和 测试文件
  • preprocessors 对 files 的预处理

我们在 src/common 中建立 utils.js 作为被测试的 Js 文件

// add.js
export const add = (x, y) => {
    console.log(1123);
    return x + y;
};

在 test/unit/specs/common 中建立 utils.spec.js 作为对应的测试文件

import assert from 'assert';
import {add} from '@/common/utils';

describe('#utils.js', () => {

    describe('#add()', () => {

        it("1加1应该等于2", function () {
            assert.strictEqual(add(1, 1), 2);
        });

    });

});

修改 karma.conf.js,test/unit/index.js和webpack.test.conf.js

// karma.conf.js

const webpackConfig = require('../../build/webpack.test.conf');
module.exports = function (config) {
    config.set({
        browsers: ['ChromeHeadless'],
        frameworks: ['mocha'],
        reporters: ['progress'],
        files: ['./index.js'],
        preprocessors: {
            './index.js': ['webpack', 'sourcemap']
        },
        webpack: webpackConfig,
        webpackMiddleware: {
            logLevel: 'error'
        },
        plugins: [
            'karma-chrome-launcher',
            'karma-mocha',
            'karma-sourcemap-loader',
            'karma-webpack'
        ]
    })
};
// index.js

const testsContext = require.context('./specs', true, /\.spec$/);
testsContext.keys().forEach(testsContext);

const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/);
srcContext.keys().forEach(srcContext);
// webpack.test.conf.js

'use strict'
// This is the webpack config used for unit tests.

const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')

const webpackConfig = merge(baseWebpackConfig, {
    devtool: '#inline-source-map'
});

delete webpackConfig.entry;

module.exports = webpackConfig;

其中 index.js 中分两部分 引入了 测试文件和被测试的文件

karma.conf.js 中的 webpack 和 webpackMiddleware 部分是 karma-webpack 提供的配置属性

为了方便, 我们可以在 package.json 中加入

"test": "karma start test/unit/karma.conf.js"

最后运行

npm run test

应该可以看到一条测试通过的消息

unit-test-001.png

二. 加入 Chai 断言库

Chai 断言库 可以让我们写出 高可读性的测试案例

安装 Chai, 并在 karma.conf.js 中的plugins里添加 karma-chai, framework 中添加 chai

npm i chai karma-chai -D

之后我们就可以使用 chai 断言库的写法了

expect(add(1, 1)).to.equal(2);

三. 引入 Vue-Test-Utils 测试 Vue 文件

之前我们的测试都是简单的纯 js 文件,下面我们测试一下 vue 文件渲染的是否正确。

// HelloWorld.spec.js

import Vue from 'vue';
import HelloWorld from '@/components/HelloWorld';

describe('# src/components/HelloWorld/index.vue', () => {

    describe('# 初始化渲染测试', () => {

        it("标题应渲染为 'Welcome to Your Vue.js App' ", () => {
            const Constructor = Vue.extend(HelloWorld);
            const vm = new Constructor().$mount();
            expect(vm.$el.querySelector("h1").textContent).to.equal("Welcome to Your Vue.js App")
        });

    });

});

安装 Vue-Test-Utils 可以为我们减少很多代码量

npm i @vue/test-utils -D

使用 Vue-Test-Utils 重写 HelloWorld.spec.js 之后

import {shallowMount} from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld';

describe('# src/components/HelloWorld/index.vue', () => {

    describe('# 初始化渲染测试', () => {

        it("标题应渲染为 'Welcome to Your Vue.js App' ", () => {
            const wrapper = shallowMount(HelloWorld, {});
            expect(wrapper.find("h1").text()).to.equal("Welcome to Your Vue.js App")
        });
    });
});

加入 Sinon , 实现测试替代

Sinon 可以实现测试替代, 用来消除测试的复杂度。

首先安装 sinon , sinon-chai , karma-sinon-chai 并将 karma-sinon-chai 加入到 karma.conf.js 的 plugins 和 frameworks 中

npm i sinon sinon-chai karma-sinon-chai -D

之后我们就可以使用 Sinon 进行测试了 , Sinon 中有 Spy、Stub、Mock 三个常用概念。 下面主要记录下 Spy 和 Stub 的使用。

  • Spy 是指 根据已有函数 生成一个间谍函数,它会记录下函数调用的参数,返回值,this的值,以及抛出的异常。

使用 Spy 记录 某个方法的调用次数

import {mount, createLocalVue} from '@vue/test-utils';
import api from '@/api';
import HelloWorld from '@/components/HelloWorld';
import sinon from 'sinon';

describe('# src/components/HelloWorld/index.vue', () => {

    const localVue = createLocalVue();
    localVue.use(api);

    describe('# Sinon', () => {

        it("按钮点击后 getMessage 方法 应被调用", async () => {
            const spy = sinon.spy(HelloWorld.methods, 'getMessage');
            const wrapper = mount(HelloWorld, {localVue});
            wrapper.find('button').trigger('click');
            wrapper.vm.$nextTick().then(() => {
                expect(spy.called).to.equal(true);
            });
            sinon.restore();
        });
    });
});
  • Stub 可以说是 spy 的加强版,他还能额外操作函数的行为。

使用 Stub 修改某个函数的返回值,跳过原函数的运行。(常用于处理 ajax、异步等)

import {mount, createLocalVue} from '@vue/test-utils';
import api from '@/api';
import HelloWorld from '@/components/HelloWorld';
import sinon from 'sinon';

describe('# src/components/HelloWorld/index.vue', () => {

    const localVue = createLocalVue();
    localVue.use(api);

    describe('# 初始化渲染测试', () => {

        it("按钮点击后 getMessage 方法 应被调用", async () => {
            const spy = sinon.spy(HelloWorld.methods, 'getMessage');
            const wrapper = mount(HelloWorld, {localVue});
            wrapper.find('button').trigger('click');
            wrapper.vm.$nextTick().then(() => {
                expect(spy.called).to.equal(true);
            });
            sinon.restore();
        });

    });
});

加入 karma-spec-reporter, karma-coverage ,istanbul 更好的查看测试结果并统计测试代码覆盖率

首先安装 karma-spec-reporter,karma-coverage, istanbul 并将 karma-spec-reporter,karma-coverage 加入到 karma.conf.js 的 plugins 和 reporters 中,将 babel-plugin-istanbul 加入到 .babelrc 中

npm i karma-coverage karma-spec-reporter babel-plugin-istanbul -D

调整 karma.conf.js 配置文件

// karma.conf.js
module.exports = function (config) {
     config.set({
        // Other setting...

        reporters: ['spec', 'coverage'],
    });
});

调整 .babelrc 文件, 增加测试环境下的 istanbul 插件

"env": {
    "test": {
      "presets": [
        "env",
        "stage-2"
      ],
      "plugins": [
        "istanbul"
      ]
    }
  }

运行 npm run test 控制台就可以打印出清晰的测试结果和代码覆盖率了

代码地址:https://github.com/geminate/vue-unit-test-learn

文章导航