为什么要进行测试
- 测试可以确保得到预期的结果
- 作为现有代码⾏为的描述
- 促使开发者写可测试的代码,⼀般可测试的代码可读性也会⾼⼀点
- 如果依赖的组件有修改,受影响的组件能在测试中发现错误
测试分类
单元测试
:指的是以原件的单元为单位,对软件进⾏测试。单元可以是⼀个函数,也可以是⼀个模块或⼀个组件,基本特征就是只要输⼊不变,必定返回同样的输出。⼀个软件越容易些单元测试,就表明它的模块化结构越好,给模块之间的耦合越弱。React的组件化和函数式编程,天⽣适合进⾏单元测试
功能测试
:相当于是⿊盒测试,测试者不了解程序的内部情况,不需要具备编程语⾔的专⻔知识,只知道程序的输⼊、输出和功能,从⽤户的⻆度针对软件界⾯、功能和外部结构进⾏测试,不考虑内部的逻辑
集成测试
:在单元测试的基础上,将所有模块按照设计要求组装成⼦系统或者系统,进⾏测试
冒烟测试
:在正式全⾯的测试之前,对主要功能进⾏的与测试,确认主要功能是否满⾜需要,软件是否能正常运⾏
组件的单元测试有很多好处:
- 提供描述组件⾏为的⽂档
- 节省⼿动测试的时间
- 减少研发新特性时产⽣的 bug
- 改进设计
- 促进重构
测试⼯具
2.jpg单测
单元测试(unit testing),是指对软件中的最⼩可测试单元进⾏检查和验证。
在vue中,推荐⽤Mocha+chai 或者jest,咱们使⽤jest演示,语法基本⼀致
新建kaikeba.spec.js,.spec.js是命名规范,写下⼀下代码
function add(num1, num2) {
return num1 + num2
}
describe('Kaikeba', () => {
it('测试加法', () => {
expect(add(1, 3)).toBe(3)
expect(add(1, 3)).toBe(4)
expect(add(-2, 3)).toBe(1)
})
})
执⾏ npm run test:unit
3.jpg
api介绍
- describe : 定义⼀个测试套件
- it :定义⼀个测试⽤例
- expect :断⾔的判断条件
- toBe :断⾔的⽐较结果
测试Vue组件
<template>
<div>
<span>{{ message }}</span>
<button @click="changeMsg">点击</button>
</div>
</template> <script>
export default {
data () {
return {
message: 'vue-text'
}
},
created () {
this.message = '开课吧'
},
methods:{
changeMsg(){
this.message = '按钮点击'
}
}
}
</script>
// 导⼊ Vue.js 和组件,进⾏测试
import Vue from 'vue'
import KaikebaComp from '@/components/Kaikeba.vue'
// 这⾥是⼀些 Jasmine 2.0 的测试,你也可以使⽤你喜欢的任何断⾔库或测试⼯具。
describe('KaikebaComp', () => {
// 检查原始组件选项
it('由created⽣命周期', () => {
expect(typeof KaikebaComp.created).toBe('function')
})
// 评估原始组件选项中的函数的结果
it('初始data是vue-text', () => {
expect(typeof KaikebaComp.data).toBe('function')
const defaultData = KaikebaComp.data()
expect(defaultData.message).toBe('hello!')
})
})
4.jpg
检查mounted之后
it('mount之后测data是开课吧', () => {
const vm = new Vue(KaikebaComp).$mount()
expect(vm.message).toBe('开课吧')
})
用户点击
和写vue 没啥本质区别,只不过我们⽤测试的⻆度去写代码,vue提供了专⻔针对测试的 @vue/test-utils,可以区官网查看更多API
it('按钮点击后', () => {
const wrapper = mount(KaikebaComp)
wrapper.find('button').trigger('click')
expect(wrapper.vm.message).toBe('按钮点击')
// 测试html渲染结果
expect(wrapper.find('span').html()).toBe('<span>按钮点击</span>')
})
测试覆盖率
jest⾃带覆盖率,如果⽤的mocha,需要使⽤istanbul来统计覆盖率
package.json⾥修改jest配置
"jest": {
"collectCoverage": true,
"collectCoverageFrom": ["src/**/*.{js,vue}"],
}
在此执⾏npm run test:unit
5.jpg可以看到我们kaikeba.vue的覆盖率是100%,我们修改⼀下代码
<template>
<div>
<span>{{ message }}</span>
<button @click="changeMsg">点击</button>
</div>
</template> <script>
export default {
data() {
return {
message: "vue-text",
count: 0
};
},
created() {
this.message = "开课吧";
},
methods: {
changeMsg() {
if (this.count > 1) {
this.message = "count⼤于1";
} else {
this.message = "按钮点击";
}
},
changeCount() {
this.count += 1;
}
}
};
</script>
6.jpg
现在的代码,依然是测试没有报错,但是覆盖率只有66%了,⽽且没有覆盖的代码⾏数,都标记了出来
Jest详解
beforeAll(() => {
console.log('global before all');
});
afterAll(() => {
console.log('global after all');
});
beforeEach(() =>{
console.log('global before each');
});
afterEach(() => {
console.log('global after each');
});
describe('test1', () => {
beforeAll(() => {
console.log('test1 before all');
});
afterAll(() => {
console.log('test1 after all');
});
beforeEach(() => {
console.log('test1 before each');
});
afterEach(() => {
console.log('test1 after each');
});
it('test sum', () => {
expect(sum(2, 3)).toEqual(5);
});
it('test mutil', () => {
expect(sum(2, 3)).toEqual(7);
});
});
断言
- expect(value):要测试⼀个值进⾏断⾔的时候,要使⽤expect对值进⾏包裹
- toBe(value):使⽤Object.is来进⾏⽐较,如果进⾏浮点数的⽐较,要使⽤toBeCloseTo
- not:⽤来取反
- oEqual(value):⽤于对象的深⽐较
- toMatch(regexpOrString):⽤来检查字符串是否匹配,可以是正则表达式或者字符串
- toContain(item):⽤来判断item是否在⼀个数组中,也可以⽤于字符串的判断
- toBeNull(value):只匹配null
- toBeUndefined(value):只匹配undefined
- toBeDefined(value):与toBeUndefined相反
- toBeTruthy(value):匹配任何使if语句为真的值
- toBeFalsy(value):匹配任何使if语句为假的值
- toBeGreaterThan(number): ⼤于
- toBeGreaterThanOrEqual(number):⼤于等于
- toBeLessThan(number):⼩于
- toBeLessThanOrEqual(number):⼩于等于
- toBeInstanceOf(class):判断是不是class的实例
- anything(value):匹配除了null和undefined以外的所有值
- resolves:⽤来取出promise为fulfilled时包裹的值,⽀持链式调⽤
- rejects:⽤来取出promise为rejected时包裹的值,⽀持链式调⽤
- toHaveBeenCalled():⽤来判断mock function是否被调⽤过
- toHaveBeenCalledTimes(number):⽤来判断mock function被调⽤的次数
- assertions(number):验证在⼀个测试⽤例中有number个断⾔被调⽤
- extend(matchers):⾃定义⼀些断⾔
方法
- simulate(event, mock):模拟事件,⽤来触发事件,event为事件名称,mock为⼀个event object
- instance():返回组件的实例
- find(selector):根据选择器查找节点,selector可以是CSS中的选择器,或者是组件的构造函数,
组件的display name等 - at(index):返回⼀个渲染过的对象
- get(index):返回⼀个react node,要测试它,需要重新渲染
- contains(nodeOrNodes):当前对象是否包含参数重点 node,参数类型为react对象或对象数组
- text():返回当前组件的⽂本内容
- html(): 返回当前组件的HTML代码形式
- props():返回根组件的所有属性
- prop(key):返回根组件的指定属性
E2E测试
借⽤浏览器的能⼒,站在⽤户测试⼈员的⻆度,输⼊框,点击按钮等,完全模拟⽤户,这个和具体的框架关系不⼤,完全模拟浏览器⾏为.
也被称为端到端测试,启动之后会启动一个类似于浏览器的窗口自动执行,因为需求变更等原因,E2E代码测试编码多,甚至可能为了测试一行代码需要写十行测试代码,针对多变的场景不适合,最多针对登录管理等功能固定的页面可以写一些E2E测试。
对比单元测试:单元测试针对vue甚至可以针对到单一组件,更具象,所以一般只是写单元测试。
修改e2e/spec/test.js
// https://docs.cypress.io/api/introduction/api.html
describe('端到端测试,抢测试⼈员的饭碗', () => {
it('先访问⼀下', () => {
cy.visit('/')
// cy.contains('h1', 'Welcome to Your Vue.js App')
cy.contains('#message', '开课吧')
})
})
7.jpg
可以看到是打开了⼀个浏览器进⾏测试
测试⽤户点击
// https://docs.cypress.io/api/introduction/api.html
describe('端到端测试,抢测试⼈员的饭碗', () => {
it('先访问⼀下', () => {
cy.visit('/')
// cy.contains('h1', 'Welcome to Your Vue.js App')
cy.contains('#message', '开课吧')
cy.get('button').click()
cy.contains('#message', '按钮点击')
})
})
TDD
所以TDD 就是测试驱动开发模式,就是我们开发⼀个新功能,先把测试写好,然后测试跑起来,会报错,我们再开始写代码,挨个的把测试跑绿,功能也就完成了
React 自动化测试
React中,也是使⽤jest来做⾃动化测试
https://jestjs.io/docs/en/tutorial-react
总结
测试会极大拖慢开发进度,但是如果测试一旦也写好。例如单元测试,在package.json文件中配置了,则每次保存或者构建之前都会执行一下测试的函数,例如写了add函数,多成员开发,B修改了该函数,则测试不通过,连项目都跑不起来,则保证了项目的健壮性。
代码地址:https://gitee.com/zengqiang_455/qianduanzidonghuaceshixiaodemo
网友评论