对软件中的最小可测试单元(一个方法)进行测试
1、分模块开发,方便定位到哪个模块出现问题
2、保证了代码质量
3、驱动开发(先写单元测试,通过再写代码)
TDD(测试驱动开发,从需求角度出发)
我需要的结果是什么?如果不是即为错误
针对于数据,面向的是最终的值,适用于开发人员
开发流程:需求分析->编写单元测试->编写代码使单元测试通过->重构
BDD(行为驱动开发,从具体功能角度出发)
结果应该是什么?如果不是即为错误
面向的是最终的产出效果,适用于需求客户人员
开发流程:从业务角度定义目标->找到实现目标的方法->编写单元测试->实现行为->检查产品
测试框架
帮助测试得以运行
Jest-基于Jasmine,对react友好
Jasmine-BDD风格,自带assert和mock(大集成)
Mocha-全面适合node和浏览器两端
Qunit-出自jquery,后来独立出来
断言库(assert)
帮助检查结果
chai-支持所有风格,全面
Assert-node环境直接使用
Mock库
帮助屏蔽方法、数据及相关依赖,避免外部数据对测试产生影响
sinon
test runner
提供运行环境,使得方法能够运行
karma
覆盖率工具
测试比例,明确测试了多少内容
istanbul
测试用例(测试人员编写)
为了某个特殊目标而编织的一系列测试输入,执行以及输出结果,来测试是否满足需求
单元测试(前端编写)
1、脚手架安装项目,选择单元测试
2、生成目录如下
karma.conf.js配置文件说明
var webpackConfig = require('../../build/webpack.test.conf')
module.exports = function karmaConfig (config) {
config.set({
//模拟环境
browsers: ['PhantomJS'],//无头浏览器,不属于任何厂家,单纯的一个javascript执行环境
//所用到的各种库,mocha框架,sinon-chai断言库
frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
//覆盖率暴露到哪里
reporters: ['spec', 'coverage'],
//需要加载到浏览器的文件
files: ['./index.js'],
//使用前的处理
preprocessors: {
'./index.js': ['webpack', 'sourcemap']
},
webpack: webpackConfig,
webpackMiddleware: {
noInfo: true
},
//指定报告输出的类型和目录
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' }
]
}
})
}
index.js文件说明
import Vue from 'vue'
Vue.config.productionTip = false
//require.context将指定路径下所有符合规则的文件都引入
const testsContext = require.context('./specs', true, /\.spec$/)
testsContext.keys().forEach(testsContext)
//业务代码引入
const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
srcContext.keys().forEach(srcContext)
3、编写单元测试
.spec.js文件下编写单元测试
import Vue from 'vue'
import HelloWorld from '@/components/HelloWorld'
import axios from 'axios'
import { expect } from 'chai'
//整个组件测试描述
describe('HelloWorld.vue', () => {
//拿到当前组件的构造函数
const Constructor = Vue.extend(HelloWorld)
//挂载,模拟渲染过程
const vm = new Constructor().$mount()
//每一个it就是对于一个方法的单元测试
//描述测试
it('should render correct contents', () => {
expect(vm.$el.querySelector('.hello h1').textContent)
.to.equal('Welcome to Your Vue.js App')
})
//检测一个方法
it('m1 should add two arguments', () => {
const m1 = vm.m1
expect(m1(2, 3)).to.equal(5)
})
//检测数据更新
it('should change correct content', () => {
vm.m2();
//需要等到页面加载完毕,再去进行数据更改
Vue.nextTick(()=>{
expect(vm.$el.querySelector('.hello h1').textContent)
.to.equal('123')
})
})
//检测异步方法
it('async m3 should return 6', () => {
vm.m3(2, 4, (num) => {
expect(num).to.equal(6)
})
})
//检测接口请求
it('api request', () => {
//屏蔽真正的请求
let axiosstub = sinon.stub(axios,'get')
//提供模拟真正请求的数据
let callback = sinon.spy(() => {
return 5
})
const getmes = vm.getmes
const finalData = getmes(callback)
expect(finalData).to.equal(4)
})
})
组件内编写方法
<script>
import axios from 'axios'
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
methods:{
m1(a,b){
return a+b
},
m2(){
this.msg = 123
},
m3(a, b, cb){//入侵一个cb
setTimeout(()=>{
cb(a + b)
}, 2000)
},
getmes(cb){
axios.get('/aaa/bbb')
let res = cb()
res-=1
return res
}
}
}
</script>