Skip to content

10.Vue的自动化测试 #14

@brandonxiang

Description

@brandonxiang

前言

为什么我们需要测试?

让产品可以快速迭代,同时还能保持高质量 -- 阮一峰 持续集成是什么?

对于一些相对稳定的系统级别页面,自动化测试在提高测试的效率的方面起到非常重要的作用。前端的自动化测试主要包括:浏览器测试和单元测试。Vue官方脚手架自带自动化测试配置,并帮助你完成对组件,函数等的自动化测试。

什么是持续集成?它和持续部署有什么区别?

代码集成到主分支需要经过一系列的自动化测试,当测试都通过之后,方可执行自动化部署,否则不能完成集成。这说明了自动化测试的重要性,我们不能等测试工程师去发现问题。

在Vue脚手架当中,Karma和NightWatch分别对应着单元测试和e2e测试。单元测试更多是面向JS功能逻辑的检验,而NightWatch更多是面对业务逻辑的检验。

单元测试

代码的单元测试主要针对某些核心功能的某些函数进行测试。vue官方推荐是使用karma,mocha和chai等。karma并不是一个测试框架,也不是一个断言库。它可以运行HTTP Server,运行HTML文件在你喜欢的测试框架上。不仅仅只是运行测试,还可以计算测试的覆盖率。mocha是测试框架,专门实现各个单元划分测试。chai是典型的断言库。

vue单元测试

npm run unit # 运行单元测试

Karma

Karma是一个专门的测试运行器(runner),它不是一个测试框架框架,也不是以一个断言库。Karma兼容JasmineMochaQUnit,可以集成mocha,webpack等功能,成为以Karma为平台的单元测试,官方选择的事mocha的测试框架和chai的断言库。它拥有一些测试插件:

karma-coverage是基于istanbul。这些插件集成在karma这个runner,把webpack打包的vue项目,测试里组件实现的功能,包括组件库,业务逻辑和请求范围。它的配置文件karma.conf.js里面默认配置了很多插件的内容,基本上不需要调整。

var webpackConfig = require('../../build/webpack.test.conf');

module.exports = function (config) {
  config.set({
    browsers: ['PhantomJS'], // 测试服务浏览器
    frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], // 测试框架
    reporters: ['spec', 'coverage'], // 报告输出
    files: ['./index.js'], // 文件入口,过滤掉index.html
    preprocessors: {
      './index.js': ['webpack', 'sourcemap'] // 预处理器
    },
    webpack: webpackConfig, // 专门测试webpack配置
    webpackMiddleware: {
      noInfo: true,
    },
    coverageReporter: {
      dir: './coverage', // 覆盖率配置
      reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' },
      ]
    },
  });
};

Mocha

mocha本身只是一个单元测试框架,可以兼容第三方断言库,包括:

在Vue开发框架中位于specs(specifications)文件底下,可以针对某个组件或者某个js进行测试。

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

describe('Hello.vue', () => {
  it('should render correct contents', () => {
    const Constructor = Vue.extend(Hello);
    const vm = new Constructor().$mount();
    expect(vm.$el.querySelector('.hello h1').textContent)
      .to.equal('Welcome to Your Vue.js App');
  });
});

其中,describeit是mocha的语法结构,describe是这对某个组件或者函数的名字描述,测试脚本里面应该包括一个或多个describe块,每个describe块应该包括一个或多个it块。it是对它需要完成某些功能的描述,它里面是具体的测试用例。在测试框架中,describe,it, expectsinon都是全局方法。

Chai

Chai也就是一个非常简单的断言库,所谓的断言,就是预期某些执行结果符合你自己的要求。所有的测试用例都应该含有一句或多句的断言。

expect(vm.$el.querySelector('.hello h1').textContent)
      .to.equal('Welcome to Your Vue.js App');

上面这句话的含义,就是在实例化了Hello这个组件之后,针对它某个dom上绑定的文字进行断言,保证了数据绑定后的文字显示。同理,也可以使用在某些js实现某个功能,对它进行自动化测试。

vue-test-utils

vuejs/vue-test-utils是官方的辅助测试库。它其实即是将组件实例化的过程进行封装,帮你模拟它组件实例化过程。shallow这个函数把组件List实例化,并传入props等参数,这样,你可以断言数据绑定的结果。

const items = ['', '']
const wrapper = shallow(List, {
    propsData: { items }
})
expect(wrapper.findAll('li').length).to.equal(items.length)

vue-router和vuex的单元测试问题

项目往往都是使用vuex和vue-router进行异步获取数据,需要外部依赖。这时,我们需要inject-loader来实现对vue组件对象内部的数据模拟。假设我们有这么一个组件,它引用了一个服务,可以是同步或异步(promise),这个对象会被整体替换。

<!-- example.vue -->
<template>
  <div class="msg">{{ msg }}</div>
</template>

<script>
// this dependency needs to be mocked
import SomeService from '../service'

export default {
  data () {
    return {
      msg: SomeService.msg
    }
  }
}
</script>

首先你要安装inject-loader,通过行内写法注入该组件。

npm install inject-loader@^2.0.0 --save-dev

const ExampleInjector = require('!!vue-loader?inject!./example.vue')

在对应的spec.js中添加了需要注入的对象。../service是在组件中的依赖对象,它的结果会被替换。

const ExampleWithMocks = ExampleInjector({
  // mock it
  '../service': {
    msg: 'Hello from a mocked service!'
  }
})

端对端测试

单元测试更多是对某个组件或者js进行功能测试。端对端测试(e2e)用于模拟整个业务流程的进行自动化测试(填报,增删查改等)。vue官方推荐是使用nightwatch,它同样只是个runner,默认配置chromedriver,phantomjs。

NightWatch

NightWatch是一个专门的端对端测试运行器(runner),它的配置文件nightwatch.conf.js会设置对应的命令参数,拼接到nightwatch的命令行操作,详情参考官网

它依赖于浏览器控制器selenium,而selenium是一个.jar后缀的文件,需要java的运行环境。所以你需要安装java并配置好环境变量。然而,selenium需要对应的driver配合来操控浏览器。

执行测试

default e2e test类似单元测试中的describeit的测试描述,browser则是传入的浏览器对象,这个对象可以是chrome,也可以是firefox,由selenium控制。url说明的是跳到目标路径,waitForElementVisible说明的是等待5000ms直到id为app的dom出现,然后,assert帮助你进行断言某个dom的内容。

module.exports = {
  'default e2e tests': function test(browser) {
    const devServer = browser.globals.devServerURL;

    browser
      .url(devServer)
      .waitForElementVisible('#app', 5000)
      .assert.elementPresent('.hello')
      .assert.containsText('h1', 'Welcome to Your Vue.js App')
      .assert.elementCount('img', 1)
      .end();
  },
};

综上所述,e2e测试的写法很简单,它相较于单元测试得功能检验,更多是浏览器外的控制,针对某些业务流程进行浏览器的操作验证。以前测试工程师要花十分钟完成的“测试流程”,我们可以快速得模拟出来。

参考

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions