一、测试管理工具的理解
在JavaScript中至少需要三个工具进行测试
测试管理工具(如karma)
- 组织和运行整个测试的工具
- 将测试框架、断言库、测试浏览器、测试代码和被测试代码组织起来,并运行被测试代码进行测试。
- 测试工具有很多选择,Selenium、WebDriver/Selenium 2、Mocha[1]、JsTestDriver、HTML Runners和Karma,我这里选择使用Karma。
- 一般情况下回提供测试覆盖率统计工具(
Karma-Coverage)
测试框架
提供了单元测试所需的各种API,你可以使用它们来对你的代码进行单元测试
测试断言库
断言库提供了用于描述你的具体测试的API,有了它们你的测试代码便能简单直接,也更为语义化
测试浏览器
前端代码是运行在浏览器中的,要对其进行单元测试,只能将其运行在浏览器上。
目前大部分测试工具都支持调用和运行本地浏览器来进行测试,但如果你的测试仅仅是针对函数和模块的单元测试,则完全可以使用一款无界面的浏览器:PhantomJs
二、使用Karma + Mocha 做单元测试
- karma 是一个测试运行器,呼起浏览器,加载测试脚本,运行测试用例
- Mocha 是一个单元测试框架/库,可以用来写测试用例(断言,describe、it)
- 简单来说,单元测试降低了后期维护代码的时间成本
三、安装工具
npm i -D karma karma-chrome-launcher karma-mocha karma-sinon-chai mocha sinon sinon-chai karma-chai karma-chai-spies
四、配置karma
// 新建 karma.conf.js,内容如下
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'sinon-chai'],
client: {
chai: {
includeStack: true
}
},
// list of files / patterns to load in the browser
files: [
'dist/**/*.test.js',
'dist/**/*.test.css'
],
// list of files / patterns to exclude
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['ChromeHeadless'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
五、创建test/button.test.js 文件
const expect = chai.expect;
import Vue from 'vue'
import Button from '../src/button'
Vue.config.productionTip = false
Vue.config.devtools = false
describe('Button', () => {
it('存在.', () => {
expect(Button).to.be.ok
})
it('可以设置icon.', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings'
}
}).$mount()
const useElement = vm.$el.querySelector('use')
expect(useElement.getAttribute('xlink:href')).to.equal('#i-settings')
vm.$destroy()
})
it('可以设置loading.', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
loading: true
}
}).$mount()
const useElements = vm.$el.querySelectorAll('use')
expect(useElements.length).to.equal(1)
expect(useElements[0].getAttribute('xlink:href')).to.equal('#i-loading')
vm.$destroy()
})
it('icon 默认的 order 是 1', () => {
const div = document.createElement('div')
document.body.appendChild(div)
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
}
}).$mount(div)
const icon = vm.$el.querySelector('svg')
expect(getComputedStyle(icon).order).to.eq('1')
vm.$el.remove()
vm.$destroy()
})
it('设置 iconPosition 可以改变 order', () => {
const div = document.createElement('div')
document.body.appendChild(div)
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
iconPosition: 'right'
}
}).$mount(div)
const icon = vm.$el.querySelector('svg')
expect(getComputedStyle(icon).order).to.eq('2')
vm.$el.remove()
vm.$destroy()
})
it('点击 button 触发 click 事件', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
}
}).$mount()
const callback = sinon.fake();
vm.$on('click', callback)
vm.$el.click()
expect(callback).to.have.been.called
})
})
六、创建测试脚本
package.json 里面找到 scripts 并改写 scripts
"scripts": {
"dev-test": "parcel watch test/* --no-cache & karma start",
"test": "parcel build test/* --no-minify && karma start --single-run"
},
七、在入口文件引入chai和spices
import chai from 'chai'
import spies from 'chai-spies'
chai.use(spies)
八、运行测试脚本
npm run test
九、测试方法套路
//创建构造函数,生成实例,传递需要测试的参数,mount到文档中
//获取元素,获取元素的属性,使用断言,destroy实例
const Constructor = Vue.extend(Button)
let vm = new Constructor({
propsData: {
icon: 'setting',
}
}).$mount()
let useElement = vm.$el.querySelector('use')
expect(useElement[0].getAttribute('').to.eq(''))
// 同上,需要测试css就需要mount到div在mount到文档中
// 获取css属性,getComputedStyle
it('icon 默认的 order 是 1', () => {
const div = document.createElement('div')
document.body.appendChild(div)
const Constructor = Vue.extend(Button)
const vm = new Constructor({
propsData: {
icon: 'settings',
}
}).$mount(div)
const icon = vm.$el.querySelector('svg')
expect(getComputedStyle(icon).order).to.eq('1')
vm.$el.remove()
vm.$destroy()
})
// 同上,需要测试触发事件
// 伪造回调
it('点击 button 触发 click 事件', () => {
const Constructor = Vue.extend(Button)
const vm = new Constructor().$mount()
const callback = sinon.fake();
vm.$on('click', callback)
vm.$el.click()
// vm.$el此时相当于组件
expect(callback).to.have.been.called
})
参考:
前端自动化单元测试初探