一、前言
我们在vue项目中Vue Test Utils 测试环境已经搭建完成的基础上来进行演示,搭建环境可以看我之前的文章。
本文中使用的相关技术点连接:
测试环境: Vue Test Utils
断言库:chai
二、基础
1、describe:
存放测试用例的容器,一个describe中可以定义多个测试用例。
语法
describe('说明', () => {
// 函数中写测试用例
})
2、it:
编写测试用例
语法
it('描述', () => {
//函数中编写断言(对结果的预期)
})
3、运行
1 package.json中的scripts对象中配置"unit": "jest --config test/unit/jest.config.js",
2 项目 跟目录运行npm run unit
三、
1、第一个测试用例演示 创建测试文件 test.spec.js
describe('测试', () => {
it('测试用例', () => {
// 演示一下,断言字符串'hello'内容是'hello',当然我这么写,结果肯定是true的。
// epect后面的字符串以后会变成真正的页面内容。
expect('hello').toContain('hello')
})
})
运行npm run unit
我们看到结果显示已经通过测试
2、挂载和测试
创建counter.vue
// counter.vue
<template>
<div>
<span class="count">{{ count }}</span>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
<style>
</style>
编写测试文件counter.spec.js
import { mount } from '@vue/test-utils'
import Counter from '@/components/counter'
describe('测试counter组件', () => {
// 现在挂载组件,你便得到了这个包裹器
const wrapper = mount(Counter)
it('测试是否渲染了<span class="count">0</span>', () => {
expect(wrapper.html()).toContain('<span class="count">0</span>')
})
// 也便于检查已存在的元素
it('测试是否存在一个按钮', () => {
expect(wrapper.find('button').exists()).toBe(true)
})
// 模拟用户交互
it('单击按钮将增加计数', () => {
// 测试组件中count的初始值是0
expect(wrapper.vm.count).toBe(0)
const button = wrapper.find('button')
// 触发按钮点击事件
button.trigger('click')
// 点击累加按钮后 count的值应该变为1
expect(wrapper.vm.count).toBe(1)
})
// 页面dom解构发生变化以后需要等页面更新后才能拿到内容。
it('单击按钮将增加计数文本', async () => {
// 此处count已经不是0了,上面已经点击了一次,此时变为1
expect(wrapper.text()).toContain('1')
const button = wrapper.find('button')
await button.trigger('click')
expect(wrapper.text()).toContain('2')
})
// 断言触发方法
it('断言触发increment方法后的结果', async () => {
expect(wrapper.text()).toContain('2')
wrapper.vm.increment()
// 触发事件后断言页面信息
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('3')
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('3')
})
// 断言触发的事件
it('断言触发事件后的结果', async () => {
wrapper.vm.$emit('foo')
// 断言事件已经被触发
expect(wrapper.emitted().foo). toBeTruthy()
})
// 组件不会自动注销,我们需要手动注销
// wrapper.destroy()
})
3、父子组件
创建子组件ChildComponent.vue
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
创建父组件ParentComponent.vue
<template>
<div>
<child-component @custom="onCustom" />
<p v-if="emitted">Emitted!</p>
<div>
<p>{{msg}}</p>
<p>{{title}}</p>
<p>{{aProp}}</p>
</div>
</div>
</template>
<script>
import ChildComponent from './ChildComponent'
export default {
name: 'ParentComponent',
components: { ChildComponent },
data() {
return {
emitted: false,
msg: ''
}
},
props: ['title', 'aProp'],
methods: {
onCustom() {
this.emitted = true
}
}
}
</script>
创建测试文件childComponent.spec.js
import { mount } from '@vue/test-utils'
import ParentComponent from '@/components/ParentComponent'
import ChildComponent from '@/components/ChildComponent'
// 从子组件触发事件
describe('ParentComponent', () => {
it("displays 'Emitted!' when custom event is emitted", async () => {
// 使用包裹器载入载入渲染父组件,因为是深度渲染,所以父组件中的子组件会被渲染。
const wrapper = mount(ParentComponent)
// 在父组件中找到子组件,并且在子组件中触发自定义事件custom。
// 此时父组件中的子组件上custom事件注册的函数onCustom会被执行。
// 官网使用的是老版本find,运行后会报错语法更新
// wrapper.find(ChildComponent).vm.$emit('custom')
// 我们使用新语法findComponent
wrapper.findComponent(ChildComponent).vm.$emit('custom')
// 验证父组件中html中是否有'Emitted!'
// 此处页面dom内容发生更新需要延迟验证。
// 此处官网直接断言 expect(wrapper.html()).toContain('Emitted!'),结果当然是false。
// 此处页面dom内容发生更新需要延迟验证。
await wrapper.vm.$nextTick()
expect(wrapper.html()).toContain('Emitted!')
})
// 在测试用例中操作组件的状态
// 可以在包裹器上用 setData 或 setProps 方法直接操作组件状态:
// 在父组件中,data中增加msg,props中增加title。在dom中渲染插值表达式渲染这两项。
it('操作组件状态', async () => {
const wrapper = mount(ParentComponent)
await wrapper.setData({msg: 'hello jset'})
expect(wrapper.html()).toContain('hello jset')
await wrapper.setProps({ title: 'my title' })
expect(wrapper.html()).toContain('my title')
})
// 仿造 Prop
// 可以使用 Vue 在内置 propsData 选项向组件传入 prop:
it('伪造prop传给子组件', async () => {
const wrapper = mount(ParentComponent, {
propsData: {
aProp: 'some value'
}
})
expect(wrapper.html()).toContain('some value')
})
})
四、代码覆盖率
控制台会输出代码覆盖率:
通过分析文件查看代码覆盖率:
找到当前项目下test/unit/coverage/lcov-report/index.html,并使用浏览器运行。
%stmts是语句覆盖率(statement coverage):是不是每个语句都执行了?
%Branch分支覆盖率(branch coverage):是不是每个if代码块都执行了?
%Funcs函数覆盖率(function coverage):是不是每个函数都调用了?
%Lines行覆盖率(line coverage):是不是每一行都执行了?