Vue单元测试Karma+Mocha

Karma是一个基于Node.js的JavaScript测试执行过程管理工具(Test Runner)。该工具在Vue中的主要作用是将项目运行在各种主流Web浏览器进行测试。
换句话说,它是一个测试工具,能让你的代码在浏览器环境下测试。需要它的原因在于,你的代码可能是设计在浏览器端执行的,在node环境下测试可能有些bug暴露不出来;另外,浏览器有兼容问题,karma提供了手段让你的代码自动在多个浏览器(chrome,firefox,ie等)环境下运行。如果你的代码只会运行在node端,那么你不需要用karma。

Mocha是一个测试框架,在vue-cli中配合chai断言库实现单元测试。
Mocha的常用命令和用法不算太多,看阮一峰老师的测试框架 Mocha 实例教程就可以大致了解了。
而Chai断言库可以看Chai.js断言库API中文文档,很简单,多查多用就能很快掌握。

对测试框架的理解

npm run unit 执行过程

执行 npm run unit 命令
开启Karma运行环境
使用Mocha去逐个测试用Chai断言写的测试用例
在终端显示测试结果
如果测试成功,karma-coverage 会在 ./test/unit/coverage 文件夹中生成测试覆盖率结果的网页。

Karma的Vue的karma配置,简单注释了下:

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'],
// 预处理器 karma-webpack
preprocessors: {
'./index.js': ['webpack', 'sourcemap']
},
// Webpack配置
webpack: webpackConfig,
// Webpack中间件
webpackMiddleware: {
noInfo: true
},
// 测试覆盖率报告
// https://github.com/karma-runner/karma-coverage/blob/master/docs/configuration.md
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' }
]
}
})
}

  

Mocha和chai

import Vue from 'vue' // 导入Vue用于生成Vue实例
import Hello from '@/components/Hello' // 导入组件
// 测试脚本里面应该包括一个或多个describe块,称为测试套件(test suite)
describe('Hello.vue', () => {
// 每个describe块应该包括一个或多个it块,称为测试用例(test case)
it('should render correct contents', () => {
const Constructor = Vue.extend(Hello) // 获得Hello组件实例
const vm = new Constructor().$mount() // 将组件挂在到DOM上
//断言:DOM中class为hello的元素中的h1元素的文本内容为Welcome to Your Vue.js App
expect(vm.$el.querySelector('.hello h1').textContent)
.to.equal('Welcome to Your Vue.js App')
})
})

需要知道的知识点:
* 测试脚本都要放在 test/unit/specs/ 目录下。
* 脚本命名方式为 [组件名].spec.js。
* 所谓断言,就是对组件做一些操作,并预言产生的结果。如果测试结果与断言相同则测试通过。
* 单元测试默认测试 src 目录下除了 main.js 之外的所有文件,可在 test/unit/index.js 文件中修改。
* Chai断言库中,to be been is that which and has have with at of same 这些语言链是没有意义的,只是便于理解而已。
* 测试脚本由多个 descibe 组成,每个 describe 由多个 it 组成。
* 了解异步测试

it('异步请求应该返回一个对象', done => {
request
.get('https://api.github.com')
.end(function(err, res){
expect(res).to.be.an('object');
done();
});
}); 了解一下 describe 的钩子(生命周期)
describe('hooks', function() { before(function() {
// 在本区块的所有测试用例之前执行
}); after(function() {
// 在本区块的所有测试用例之后执行
}); beforeEach(function() {
// 在本区块的每个测试用例之前执行
}); afterEach(function() {
// 在本区块的每个测试用例之后执行
}); // test cases
});

  

实例:
util.js

从Vue官方的demo可以看出,对于Vue的单元测试需要将组件实例化为一个Vue实例,有时还需要挂载到DOM上。
const Constructor = Vue.extend(Hello) // 获得Hello组件实例
const vm = new Constructor().$mount() // 将组件挂载到DOM上

以上写法只是简单的获取组件,有时候我们需要传递props属性、自定义方法等,还有可能我们需要用到第三方UI框架。
推荐Element的单元测试工具脚本Util.js,它封装了Vue单元测试中常用的方法。下面demo也是根据该 Util.js来写的。
这里简单注释了下各方法的用途。

/**
* 回收 vm,一般在每个测试脚本测试完成后执行回收vm。
* @param {Object} vm
*/
exports.destroyVM = function (vm) {}

/**
* 创建一个 Vue 的实例对象
* @param {Object|String} Compo - 组件配置,可直接传 template
* @param {Boolean=false} mounted - 是否添加到 DOM 上
* @return {Object} vm
*/
exports.createVue = function (Compo, mounted = false) {}

/**
* 创建一个测试组件实例
* @param {Object} Compo - 组件对象
* @param {Object} propsData - props 数据
* @param {Boolean=false} mounted - 是否添加到 DOM 上
* @return {Object} vm
*/
exports.createTest = function (Compo, propsData = {}, mounted = false) {}

/**
* 触发一个事件
* 注: 一般在触发事件后使用 vm.$nextTick 方法确定事件触发完成。
* mouseenter, mouseleave, mouseover, keyup, change, click 等
* @param {Element} elm - 元素
* @param {String} name - 事件名称
* @param {*} opts - 配置项
*/
exports.triggerEvent = function (elm, name, ...opts) {}

/**
* 触发 “mouseup” 和 “mousedown” 事件,既触发点击事件。
* @param {Element} elm - 元素
* @param {*} opts - 配置选项
*/
exports.triggerClick = function (elm, ...opts) {}

示例一

示例一中测试了Hello组件的各种元素的数据,学习 util.js 的 destroyVM 和 createTest 方法的用法以及如何获取目标元素进行测试。获取DOM元素的方式可查看DOM 对象教程。

Hello.vue

<template>
<div class="hello">
<h1 class="hello-title">{{ msg }}</h1>
<h2 class="hello-content">{{ content }}</h2>
</div>
</template> <script>
export default {
name: 'hello',
props: {
content: String
},
data () {
return {
msg: 'Welcome!'
}
}
}
</script> Hello.spec.js import { destroyVM, createTest } from '../util'
import Hello from '@/components/Hello' describe('Hello.vue', () => {
let vm afterEach(() => {
destroyVM(vm)
}) it('测试获取元素内容', () => {
vm = createTest(Hello, { content: 'Hello World' }, true)
expect(vm.$el.querySelector('.hello h1').textContent).to.equal('Welcome!')
expect(vm.$el.querySelector('.hello h2').textContent).to.have.be.equal('Hello World')
}) it('测试获取Vue对象中数据', () => {
vm = createTest(Hello, { content: 'Hello World' }, true)
expect(vm.msg).to.equal('Welcome!')
// Chai的语言链是无意义的,可以随便写。如下:
expect(vm.content).which.have.to.be.that.equal('Hello World')
}) it('测试获取DOM中是否存在某个class', () => {
vm = createTest(Hello, { content: 'Hello World' }, true)
expect(vm.$el.classList.contains('hello')).to.be.true
const title = vm.$el.querySelector('.hello h1')
expect(title.classList.contains('hello-title')).to.be.true
const content = vm.$el.querySelector('.hello-content')
expect(content.classList.contains('hello-content')).to.be.true
})
})

 

Hello.vue
测试获取元素内容
测试获取Vue对象中数据
测试获取DOM中是否存在某个class

示例二中我们使用 createTest 创建测试组件测试点击事件,用 createVue 创建Vue示例对象测试组件 Click 的使用。这里主要可以看下到 createVue 方法的使用。

Click.vue

<template>
<div>
<span class="init-num">初始值为{{ InitNum }}</span><br>
<span class="click-num">点击了{{ ClickNum }}次</span><br>
<span class="result-num">最终结果为{{ ResultNum }}</span><br>
<button @click="add">累加{{ AddNum }}</button>
</div>
</template> <script>
export default {
name: 'Click',
props: {
AddNum: {
type: Number,
default: 1
},
InitNum: {
type: Number,
default: 1
}
},
data () {
return {
ClickNum: 0,
ResultNum: 0
}
},
mounted () {
this.ResultNum = this.InitNum
},
methods: {
add () {
this.ResultNum += this.AddNum
this.ClickNum++
this.$emit('result', {
ClickNum: this.ClickNum,
ResultNum: this.ResultNum
})
}
}
}
</script> Click.spec.js import { destroyVM, createTest, createVue } from '../util'
import Click from '@/components/Click' describe('click.vue', () => {
let vm afterEach(() => {
destroyVM(vm)
}) it('测试按钮点击事件', () => {
vm = createTest(Click, {
AddNum: 10,
InitNum: 11
}, true)
let buttonElm = vm.$el.querySelector('button')
buttonElm.click()
buttonElm.click()
buttonElm.click()
// setTimeout 的原因
// 在数据改变之后,界面的变化会有一定延时。不用timeout有时候会发现界面没有变化
setTimeout(done => {
expect(vm.ResultNum).to.equal(41)
expect(vm.$el.querySelector('.init-num').textContent).to.equal('初始值为11')
expect(vm.$el.querySelector('.click-num').textContent).to.equal('点击了3次')
expect(vm.$el.querySelector('.result-num').textContent).to.equal('最终结果为41')
done()
}, 100)
}) it('测试创建Vue对象', () => {
let result
vm = createVue({
template: `
<click @click="handleClick"></click>
`,
props: {
AddNum: 10,
InitNum: 11
},
methods: {
handleClick (obj) {
result = obj
}
},
components: {
Click
}
}, true)
vm.$el.click()
vm.$nextTick(done => {
expect(result).to.be.exist
expect(result.ClickNum).to.equal(1)
expect(result.ResultNum).to.be.equal(21)
done()
})
})

  

输出结果

click.vue
测试按钮点击事件
测试创建Vue对象

其他

所有示例代码都放Github仓库中便于查看。如果想查看更多好的测试用例,建议配合 Util.js
Element的单元测试脚本的写法,里面有很多测试脚本可以学习
作为被广大Vue用户使用的UI组件库,测试脚本也写很不错,对学习Vue组件的单元测试有很大帮助。

------------
Util.js 方法包含了大多数Vue组件化的测试需求。
vm.$el vm.$nextTick 和 vm.$ref 都是在测试过程中比较常用的一些Vue语法糖。
需要注意: vm.$nextTick 方法是异步的,所以需要在里面使用done方法。
异步断言,方法参数需要是 _ 或者 done
大多数时候查询元素通过 querySelector 方法查询class获得
vm.$el.querySelector(‘.el-breadcrumb’).innerText
大多数情况下查询是否存在某个Class通过 classList.contains 方法获得,查找的结果为 true 或 false
vm.$el .classList.contains(‘el-button–primary’)
异步测试必须以 done() 方法结尾。setTimeout 和 vm.$nextTick 是常用的异步测试。
实现按钮点击:通过获取按钮元素 btn,执行 btn.click() 方法实现。
由于 Vue 进行 异步更新DOM 的情况,一些依赖DOM更新结果的断言必须在 Vue.nextTick 回调中进行。

triggerEvent(vm.$refs.cascader.$el, 'mouseenter');
vm.$nextTick(_ => {
vm.$refs.cascader.$el.querySelector('.el-cascader__clearIcon').click();
vm.$nextTick(_ => {
expect(vm.selectedOptions.length).to.be.equal(0);
done();
});
});

  

Vue单元测试Karma+Mocha的更多相关文章

  1. Vue 单元测试 使用mocha+jest

    目录 Vue 单元测试 mocha+jest jest 实例 mocha expect方法断言 示例代码 Vue 单元测试 官网:https://vue-test-utils.vuejs.org/zh ...

  2. #单元测试#以karma+mocha+chai 为测试框架的Vue webpack项目(二)

    学习对vue组件进行单元测试,先参照官网编写组件和测试脚本. 1.简单的组件 组件无依赖,无props 对于无需导入任何依赖,也没有props的,直接编写测试案例即可. /src/testSrc/si ...

  3. 搭建 vue2 单元测试环境(karma+mocha+webpack3)

    从网上找了很多例子关于单元测试,都是如何新建项目的时候的添加单元测试,用vue-cli中怎么添加,但是我的项目已经生成了,不能再一次重新初始化,这时如何添加单元测试,这里面遇到了好多坑,写在这里记录一 ...

  4. 使用Karma、Mocha实现vue单元测试

    Karma Karma是一个基于Node.js的JavaScript测试执行过程管理工具(Test Runner).该工具在Vue中的主要作用是将项目运行在各种主流Web浏览器进行测试.换句话说,它是 ...

  5. #单元测试#以karma+mocha+chai 为测试框架的Vue webpack项目(一)

    目标: 为已有的vue项目搭建 karma+mocha+chai 测试框架 编写组件测试脚本 测试运行通过 抽出共通 一.初始化项目 新建项目文件夹并克隆要测试的已有项目 webAdmin-web 转 ...

  6. 大前端的自动化工厂(5)—— 基于Karma+Mocha+Chai的单元测试和接口测试

    一. 前端自动化测试 大多数前端开发者对测试相关的知识是比较缺乏的,一来是开发节奏很快,来不及写,另一方面团队里也配备了"人肉测试机",完全没必要自己来.但随着项目体量的增大,许多 ...

  7. 自动化单元测试(Karma + Mocha)

    使用 Karma + Mocha做单元测试 Karma([ˈkɑrmə] 卡玛)是一个测试运行器,它可以呼起浏览器,加载测试脚本,然后运行测试用例 Mocha([ˈmoʊkə] 摩卡)是一个单元测试框 ...

  8. 前端单元测试框架-Mocha

    引言 随着前端工程化这一概念的产生,项目开发中前端的代码量可谓是'急剧上升',所以在这种情况下,我们如何才能保证代码的质量呢,对于框架,比如React.Vue,因为有自己的语法规则,及时每个开发人员的 ...

  9. [Unit Testing] Configure the Angular CLI to use the Karma Mocha test reporter

    Every Angular CLI generated project comes already with Karmapreinstalled as well a couple of executa ...

随机推荐

  1. 自定义GridView实现分割线解析

    前两天在些项目的时候碰到常用的GridView要实现一些分割线,之前就是用本方法利用listView和Item的背景颜色的不同线显示分割线.这是最low的一种做法.于是我就简单的写了一个自定义的 Gr ...

  2. js复制文本内容到剪贴板

    记录一下使用clipboardData复制不成功. 1.定义一个按钮执行复制 <div> <button type="button" id="copyR ...

  3. TCP相关面试题(转)

    1.TCP三次握手过程 wireshark抓包为:(wireshark会将seq序号和ACK自动显示为相对值)       1)主机A发送标志syn=1,随机产生seq =1234567的数据包到服务 ...

  4. 为什么要用kafka、rabbit等消息队列

    1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险.许多消息队列所采用的&q ...

  5. 《转载》JVM垃圾回收机制

    本文转载自ImportNew - 郑雯 每个Java程序员迟早都会碰到下面这个错误: java.lang.OutOfMemoryError 这个时候一般会建议采用如下方式解决这个错误: 增加MaxPe ...

  6. ES6 扩展运算符 三点(...)

    含义 扩展运算符( spread )是三个点(...).它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列. console.log(...[, , ]) // 1 2 3 conso ...

  7. 真-关闭win10安全中心(windows defender)

    狂客原创,转载请注明.侵权必究 第一 任务管理器 启动项 禁用 第二 使用win+R,打开运行命令输入:gpedit.msc然后点击确定 在管理模块下找到Windows组件,接续打开下拉菜单,找到Wi ...

  8. Android学习:AlertDialog对话框

    AlertDialog可以生成各种内容的对话框,它生成的对话框包含4个区域:图标区,标题区,内容区,按钮区 <?xml version="1.0" encoding=&quo ...

  9. ux.form.field.Year 只能选年的时间扩展

    效果如图,亲测6.2.1版本可用,用法同时间选择控件 //只选择年的控件 Ext.define('ux.picker.Year', { extend: 'Ext.Component', alias: ...

  10. Centos 7 更换yum源

    Centos 7 更换源 yum clean all wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/ ...