VUE-UnitTest
- 本文将大致介绍vue单元测试的写法,详细请参考github代码仓库
- 单元测试写法参考Vue Test Utils,这是vue官方的单元测试实用工具库
- 使用的是jest,Facebook 开发的测试运行器
- 本文的想定读者:使用vue作为项目的开发框架,对vue有一定的理解,要写单元测试
- 开发环境,需要特别指出
- Vue-Cli:v3.3.0
- "vue": "^2.6.6"
其他,请参见package.json文件
- 特别说明
- 免责声明 本文以学习交流、技术分享为目的,因为本文内容对您产生困扰或者造成损失,笔者概不负责
- 版权声明 未经笔者同意不得用于商业用途,违者必究。个人引用请标明出处
为什么要写单元测试
组件的单元测试有很多好处:
- 提供描述组件行为的文档
- 节省手动测试的时间
- 减少研发新特性时产生的 bug
- 改进设计
- 促进重构
自动化测试使得大团队中的开发者可以维护复杂的基础代码。
以上是官方说法,就个人而言,除了以上的好处以外,单元测试还能够
- 规范代码
- 个人成就感,100%测试覆盖率(装b利器)
- 全周期工程师必备技能(设计-编程-测试-release-部署-维护)
- 如果项目是个人项目,个人测试是不可或缺的(你难道不想有一个自己的项目么)
本文提到的单元测试
Template标签内
本标签内主要是DOM结构,可以使用jest的snapshot功能进行测试,也可进行精确测试,即断言某DOM元素存在与否
- v-on(依赖函数存在于script标签内)
- v-bind(依赖变量存在于script标签内)
- slot(vue2.6.0以上版本更新了此语法,本文完成时并未影响到测试)
- v-if/v-show(依赖变量存在于script标签内)
- filter(依赖函数存在于script标签内)
测试要配合script标签的内容进行
Script标签
本标签内主要是逻辑实现,可能出现的内容有
- Props(⭐必须进行测试)
- Data(一般配合v-bind/v-if/v-show等一起测试)
- Filters(⭐必须进行测试)
- Methods(⭐必须进行测试)
- $emit(⭐必须进行测试)
- eventHub(空vue实例,用于分发事件,在使用的组件内进行测试)
- watch(⭐必须进行测试)
- computed(⭐必须进行测试)
- router(⭐必须进行测试)
- axios(⭐必须进行测试)
- vuex(⭐必须进行测试)
其他插件:element-ui/momentJS等插件本身不需要我们进行测试,开发者已经测试过,但是被测试组件如果使用了此插件的话,需要我们挂载在临时vue上,避免测试时报错
常用的断言语句
- toBe()----测试具体的值
- toEqual()----测试对象类型的值
- toBeCalled()----测试函数被调用
- toHaveBeenCalledTimes()----测试函数被调用的次数
- toHaveBeenCalledWith()----测试函数被调用时的参数
- toBeNull()----结果是null
- toBeUndefined()----结果是undefined
- toBeDefined()----结果是defined
- toBeTruthy()----结果是true
- toBeFalsy()----结果是false
- toContain()----数组匹配,检查是否包含
- toMatch()----匹配字符型规则,支持正则
- toBeCloseTo()----浮点数
- toThrow()----支持字符串,浮点数,变量
- toMatchSnapshot()----jest特有的快照测试
- .not.+matcher,eg. .not.toBe()----前面加上.not就是否定形式,
以上只是一部分matcher,更多请查看jest官方文档
项目结构
一目了然的文件树结构,能给人一种逻辑清晰的第一印象,体现了开发者的基本素养
下面是作者的文件分类方式
组件分类
组件分类不仅有利于测试,更重要的是性能优化,组件尽可能分类细化有利于快速更新视图、渲染页面、提高用户体验
前端组件分类一般分为两类
- Presentation Component
- Container Component
Presentation Component
这类组件的主要功能是DOM展示,数据一般是通过props获取数据,然后展示出来
基本特征:
- 不依赖其他组件,与store和router无关联
- 不进行CRUD操作(如果必要可使用$emit在夫组件内操作)
- 尽量不使用生命周期函数
- 尽量具有可复用性
Container Component
这类组件的主要功能是数据操作(API联调)
基本特征:
- 子组件的状态改变(props、$emit)
- CRUD操作、store内数据操作、router切换
文件树构成
基于组件分类方式,把不同的组件放在不同的文件夹内
-src
-assets------CSS,JS,image,icon
-basics------基础组件(不含其他组件,便于复用)
-components--大型组件(含其他组件,会被复用)
-containers--容器组件(包含其他组件,不会被复用,数据处理,通常用于复杂页面分担views压力)
-views--------页面组件(路由切换使用,展示页面用,数据处理)
-App.vue
-main.js
...其他
2
3
4
5
6
7
8
9
下面相应讲解一下每个文件夹内对应的组件类别
- Bsics文件夹内存放的一般是最小的组件单元和可复用的组件,比如:按钮组件AppButton.vue、输入框组件AppInput.vue 等等,属于presentation component
- Components文件夹内存放的是集合了basics或者components组件的父组件,属于presentation component
- Containers文件夹内存放的是比自身组件小的组件basics或者components组件的父组件,主要是给子组件传递props等数据,属于containers component
- views文件夹内存放的是页面组件,内涵页面内所有子组件,主要是操作数据和路由切换,属于containers component
※ 实际请根据项目的需求构建自己的文件树
单元测试的写法
以下会列举一些单元测试的写法,源码来自于github仓库,.vue文件并未展示,如遇不理解之处请参考源码
Snapshot测试
目前来说是jest专有的测试方法,测试整体的html有没有更改,每一个文件都应该进行该测试。
※ 更改html后,该测试会失败,删除保存在__snapshots__文件夹内对应的.snap文件后,重新测试(或者根据实际情况在测试命令后加 -u)
// 测试内容:snapshot->概括的测试DOM结构
it('matches snapshot', () => {
const wrapper = shallowMount(AppButton)
expect(wrapper.html()).toMatchSnapshot()
wrapper.destroy()
})
2
3
4
5
6
7
精确DOM结构测试
断言指定节点对象是否存在,视项目情况而定需不需要进行此测试
// 测试内容:精准DOM结构测试示例
it('DOM test', () => {
const wrapper = shallowMount(AppButton)
expect(wrapper.contains('button')).toBeTruthy()
wrapper.destroy()
})
2
3
4
5
6
7
v-on
- 以click事件为例
- 测试思路:点击,断言对应函数是否被触发,触发几次,参数是否为预期参数(如果有参数)
- 注意被断言的函数必须为mock函数,不然会报错
/** 测试内容:func测试
*点击按钮组件时,正确触发点击事件
*/
it('click button onClick is clled', () => {
const wrapper = shallowMount(AppButton)
// 创建mock函数
const mockFn = jest.fn()
// 设置 Wrapper vm 的方法并强制更新。
wrapper.setMethods({
onClick:mockFn
})
// 获取buttonDOM元素
const button = wrapper.find('button')
// 测试点击按钮后有没有正确触发函数
button.trigger('click')
// 断言函数被触发,且被触发一次
expect(mockFn).toBeCalled()
expect(mockFn).toHaveBeenCalledTimes(1)
wrapper.destroy()
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
v-bind(Data)
大多数情况下都可以不写此测试
/**
* 测试内容:data v-bind
* 断言data中变量的值
*/
it('data test', () => {
// 断言默认值
expect(wrapper.vm.name).toBe('admin')
expect(wrapper.vm.collapse).toBeTruthy()
// 更改后
wrapper.vm.name = 'holy'
expect(wrapper.find('.el-dropdown-link').text()).toBe('holy')
})
2
3
4
5
6
7
8
9
10
11
12
13
slot
vue2.6.0以上版本更新了此语法,本文完成时并未影响到测试
slots分为普通插槽、具名插槽和作用域插槽,以下分别示例
/**
* 测试内容:slots 普通插槽
* 测试默认值(当slot有默认值时)
*/
it('slots default value test', () => {
const wrapper = shallowMount(AppButton)
const button = wrapper.find('button')
expect(button.text()).toBe('submit')
wrapper.destroy()
})
/** 测试内容:slots 普通插槽
* mount时传入自定义的内容作为slots,然后再断言自定义的内容存在与否
* 自定义的内容可能会是text,html,componets等允许的内容
*/
it('slots test', () => {
const wrapper = shallowMount(AppButton, {
slots: {
default:'i am slots text'// 自定义slots内容
}
})
const button = wrapper.find('button')
expect(button.text()).toBe('i am slots text')
wrapper.destroy()
})
/** 测试内容:slots具名插槽
* vue2.6更新后的新语法v-slots,3.0中会延续使用,并废除旧语法
* 测试方法与slots普通插槽相同,此处传入为html,
* 当传入组件时,只需断言wrapper中是否包含组件的DOM元素即可
* expect(wrapper.contains('.container')).toBe(true)
*/
it('named slots test', () => {
const wrapper = shallowMount(AppButton, {
slots: {
namedSlot:`<span>i am slots html</span>`// 自定义slots内容
}
})
const button = wrapper.find('button')
expect(button.contains('span')).toBe(true)
const span = wrapper.find('button span')
expect(span.text()).toBe('i am slots html')
wrapper.destroy()
})
/** 测试内容:slots作用域插槽
* vue2.6更新后的新语法v-slots,3.0中会延续使用,并废除旧语法
* 测试方法与slots具名插槽相同,此处传入为html,
* 当传入组件时,只需断言wrapper中是否包含组件的DOM元素即可
* expect(wrapper.contains('.container')).toBe(true)
*/
it('scoped slots test', () => {
const wrapper = shallowMount(AppButton, {
scopedSlots: {
scopedSlot:`<span slot-scope="foo">{{ foo.user.lastName }}</span>`// 自定义slots内容
}
})
const button = wrapper.find('button')
expect(button.contains('span')).toBe(true)
constspan = wrapper.find('button span')
expect(span.text()).toBe('holy')
wrapper.destroy()
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
v-if/v-show
/**
* 测试内容:v-show/v-if测试,与v-bind的测试思路基本相同
* 设置变量的值,断言对应的DOM结构显示与否
*/
it('v-show test', () => {
// true时显示的div
const truediv = wrapper.find('.text.format')
// false时显示的div
const falsediv = wrapper.find('.text.noformat')
// toggleShow默认值为true
expect(truediv.isVisible()).toBe(true)
expect(falsediv.isVisible()).toBe(false)
// 设置为false
wrapper.vm.toggleShow = false
expect(truediv.isVisible()).toBe(false)
expect(falsediv.isVisible()).toBe(true)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
filter过滤器
- 过滤器的测试与其他略有不同,参见下方代码
import { shallowMount, createLocalVue } from '@vue/test-utils'
import FilterTest from '@/components/FilterTest.vue'
...
...
...
/** 测试内容:filter过滤器
* filter不能通过wrapper或者vm获取,只能通过组件获取
* filter需要测试函数的所有可能性
*/
it('filter test', () => {
// console.log(FilterTest.filters)
expect(FilterTest.filters.formatText('12345678')).toBe('12...78')
expect(FilterTest.filters.formatText('12345')).toBe('12345')
expect(FilterTest.filters.formatText()).toBe('')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
props
- 复杂组件的props也会较复杂,但只要思路清晰,套路都是一样的
/** 测试内容:props
* 自定义props传递给AppButton组件,判断组件有获取到props
* 请注意props是否存在默认值
*/
it('props test', () => {
const buttonProps = {
type:'danger',
size:'lg',
disabled:true
}
const wrapper = shallowMount(AppButton, {
propsData:buttonProps
})
// 断言已经获取到props
expect(wrapper.props().size).toBe('lg')
expect(wrapper.props().type).toBe('danger')
expect(wrapper.props().disabled).toBe(true)
// 每个it最后都应该销毁wrapper
wrapper.destroy()
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Methods(func函数测试)
- 主动触发函数,断言执行结果是否符合预期
/** 测试内容:changeShow()函数
* changeShow()函数被调用时,能正确执行
*/
it('called changeShow()', () => {
// 手动将变量的值设置为false,默认值是true
wrapper.vm.toggleShow = false
// 执行函数
wrapper.vm.changeShow()
// 期望结果
expect(wrapper.vm.toggleShow).toBe(true)
// 再次执行函数
wrapper.vm.changeShow()
// 期望结果
expect(wrapper.vm.toggleShow).toBe(false)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$emit
- 子组件内触发父组件内的函数,组件结构越复杂,使用的越多
- 区别于eventHub,此$emit非彼$emit
/** 测试内容:$emit
* 包含$emit的函数被触发后,emit的函数也会被触发
*/
it('when onClick is called $emit is called', () => {
const wrapper = shallowMount(AppButton)
// 测试$emmit函数被正确触发
// mock函数替代点击按钮后$emit的函数,此处函数名相同,依然为click
const mockFn1 = jest.fn()
wrapper.vm.$on('click', mockFn1)
// 测试mock函数是否被触发,触发的次数,以及参数
wrapper.vm.onClick()
expect(mockFn1).toBeCalled()
expect(mockFn1).toHaveBeenCalledTimes(1)
expect(mockFn1).toHaveBeenCalledWith('i am params')
// 第二次点击button,依然测试mock函数是否被触发,触发的次数
wrapper.vm.onClick()
expect(mockFn1).toBeCalled()
expect(mockFn1).toHaveBeenCalledTimes(2)
expect(mockFn1).toHaveBeenCalledWith('i am params')
wrapper.destroy()
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
watch
- 下例内,侦听器内仅执行了console.log()
/** 测试内容:watch
* 更改watch的data的值,断言操作是否与预期相同
*/
it('watch test', () => {
// mock掉console.log
const spy = jest.spyOn(console,'log')
// 手动将变量的值设置为false,默认值是true
wrapper.vm.toggleShow = '自定义'
// 断言函数是否执行
expect(spy).toBeCalled()
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith('自定义')
// 清除掉mock
spy.mockClear()
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
computed
⭐要注意计算属性不是函数,是变量,测试时很容易看着组件内写法按照函数测试
/** 测试内容:computed
* 要注意计算属性不是函数,是变量,测试时很容易看着组件内写法按照函数测试
* 改变props的type,size,disable值时,cssClasses的值也会跟着改变
*/
it('computed test', () => {
const wrapper = shallowMount(AppButton)
// 设置props 断言computed计算属性(注意props有default值)
wrapper.setProps({ type:'danger' })
expect(wrapper.vm.cssClasses).toBe('app-button app-button--md app-button--danger')
wrapper.setProps({ size:'lg' })
expect(wrapper.vm.cssClasses).toBe('app-button app-button--lg app-button--danger')
wrapper.setProps({ disabled:true })
expect(wrapper.vm.cssClasses).toBe('app-button app-button--lg app-button--disabled')
wrapper.destroy()
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
router
- 本文建议使用mock的$route和$router
import { shallowMount } from '@vue/test-utils'
import RouterTest from '@/components/RouterTest.vue'
/** vue-router测试
* 不建议直接在localVue上挂载vue-router
* 使用mock的$route和$router更加灵活,方便测试
*/
const $route = {
path:'/some'
// ...其他属性
}
const mockPush = jest.fn()
const $router = {
push:mockPush
// ... 其他属性
}
describe('RouterTest.vue', () => {
// wrapper.vm是组件实例,包含该实例的所有方法和属性
letwrapper
beforeEach(() => {
wrapper = shallowMount(RouterTest, {
stubs: ['app-button'],
mocks: {
$route,
$router
}
})
})
afterEach(() => {
wrapper.destroy()
})
// 测试内容:goVIPs()被调用->触发mock的push函数
it('goVIPs() is called', () => {
// 执行函数
wrapper.vm.goVIPs()
// 期望结果
expect(mockPush).toBeCalled()
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
axios
- mock掉整个axios,返回值可以在此时定义也可以在使用时定义
- .vue文件内使用axios函数内需要配合添加return,便于测试
- reject的情况也需要测试
/**
* AxiosTest.vue组件
* 测试内容包括以下
* 自定义func
* axios
*/
import { shallowMount, createLocalVue } from '@vue/test-utils'
import AxiosTest from '@/components/AxiosTest.vue'
import axios from 'axios'
// mock掉整个axios模块
// 返回值在使用的时候自定义
jest.mock('axios')
// 创建临时Vue实例,挂载组件中使用的插件
const localVue = createLocalVue()
localVue.prototype.$axios = axios// 挂载axios
describe('AxiosTest.vue', () => {
let wrapper
beforeEach(() => {
axios.mockClear()
wrapper = shallowMount(AxiosTest, { localVue,
stubs: ['app-button']
})
})
afterEach(() => {
wrapper.destroy()
})
// 测试内容:func ->getData()
// 点击按钮函数被触发(注意此处的click事件是子组件(按钮组件$emit)的事件,
// 在父组件内不属于DOM原生事件,所以触发方式不能使用trigger,而应该使用$emit)
it('when button is clicked getData will be called', () => {
// 创建mock函数
const mockFn = jest.fn()
// 设置 Wrapper vm 的方法并强制更新。
wrapper.setMethods({ getData:mockFn })
// 获取对应按钮
const axiosButton = wrapper.find('.axios app-button-stub')
// 点击按钮->注意触发方式不能使用trigger
axiosButton.vm.$emit('click')
// 断言函数被触发且只触发一次
expect(mockFn).toBeCalled()
expect(mockFn).toHaveBeenCalledTimes(1)
})
// 测试内容:axios->getData()函数
// 为了配合axios测试,需要在组件代码的两处增加return,参见AxiosTest组件
it('axios test', () => {
// 此处只是使用了get,post/patch/delete/...与get相同
// 自定义get的返回值
const mockData = { data: { name:'Bob' } }
axios.get.mockResolvedValue(mockData)
return wrapper.vm.getData().then(result=> {
expect(result).toEqual(mockData)
expect(wrapper.vm.usersInfo).toEqual(mockData.data)
})
})
// 测试内容:axios->getData()函数请求rejeced的情况
it('axios test', () => {
// 自定义get被拒绝时返回值
axios.get.mockRejectedValue('error')
return wrapper.vm.getData().catch(e=>expect(e).toMatch('error'))
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
vuex
- 伪造vuex内的所有
- 测试伪造的函数能否被正确触发
/**
* VuexTest.vue组件
* 测试内容包括以下
* 临时vue实例上挂载element-ui
* func
* vuex store内的state mutations actions getters
*/
import { shallowMount, createLocalVue } from '@vue/test-utils'
import VuexTest from '@/components/VuexTest.vue'
import Vuex from 'vuex'
// 创建临时Vue实例,挂载组件中使用的插件
const localVue = createLocalVue()
localVue.use(Vuex)
describe('VuexTest.vue', () => {
// wrapper.vm是组件实例,包含该实例的所有方法和属性
let wrapper
let actions
let state
let getters
let mutations
let store
beforeEach(() => {
// 伪造actions state getters mutations
actions = {
increment:jest.fn(),
decrement:jest.fn()
}
mutations = {
increment:jest.fn(),
decrement:jest.fn()
}
state = {
count:0
}
getters = {
evenOrOdd: () =>'gettersVal'// 伪造的越简单越好
}
// 伪造store
store = newVuex.Store({
state,
actions,
mutations,
getters
})
// 挂载store
wrapper = shallowMount(VuexTest, {
localVue,
store,
stubs: ['app-button']
})
})
afterEach(() => {
wrapper.destroy()
})
// 测试内容:state
// 只需测试伪造的state值是否存在于dom中
it('getters test', () => {
const text = wrapper.find('.text')
expect(text.text()).toContain(state.count)
})
// 测试内容:actions-通过点击按钮直接调用
// 点击按钮测试伪造的函数是否被调用
it('actions test', () => {
const buttonAdd = wrapper.find('.add')
const buttonMinus = wrapper.find('.minus')
buttonAdd.vm.$emit('click')
expect(actions.increment).toHaveBeenCalled()
expect(actions.increment).toHaveBeenCalledTimes(1)
buttonMinus.vm.$emit('click')
expect(actions.decrement).toHaveBeenCalled()
expect(actions.decrement).toHaveBeenCalledTimes(1)
})
// 测试内容:dispatchIncrement()
// 点击按钮测试函数dispatchIncrement()是否被调用
it('dispatchIncrement test', () => {
const dispatchAdd = wrapper.find('.dispatchAdd')
const mockAdd = jest.fn()
wrapper.setMethods({
dispatchIncrement:mockAdd
})
dispatchAdd.vm.$emit('click')
expect(mockAdd).toHaveBeenCalled()
expect(mockAdd).toHaveBeenCalledTimes(1)
})
// 测试内容:actions--通过dispatch调用
// 调用函数测试伪造的函数是否被调用
it('dispatch actions test', () => {
wrapper.vm.dispatchIncrement()
expect(actions.increment).toHaveBeenCalled()
expect(actions.increment).toHaveBeenCalledTimes(1)
})
// 测试内容:mutationsDecrement()
// 点击按钮测试函数mutationsDecrement()是否被调用
it('mutationsDecrement test', () => {
const mutationsMinus = wrapper.find('.mutationsMinus')
const mockMinus = jest.fn()
wrapper.setMethods({
mutationsDecrement:mockMinus
})
mutationsMinus.vm.$emit('click')
expect(mockMinus).toHaveBeenCalled()
expect(mockMinus).toHaveBeenCalledTimes(1)
})
// 测试内容:mutations
// 调用函数测试伪造的函数是否被调用
it('mutations test', () => {
wrapper.vm.mutationsDecrement()
expect(mutations.decrement).toHaveBeenCalled()
expect(mutations.decrement).toHaveBeenCalledTimes(1)
})
// 测试内容:getters
// 只需测试伪造的getters值是否存在于dom中
it('getters test', () => {
const text = wrapper.find('.text')
expect(text.text()).toContain(getters.evenOrOdd())
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
store.js
- 测试vuex内的各个函数
- 当测试其中任一函数时,其他依赖全部使用mock函数
- 该测试方法为官方推荐方法之一,官方推荐了两种测试方法,各有优劣,详情参阅官方说明
/**
* store.js
* 测试内容包括以下
* 测试思路:mutations/getters/actions 分别测试,测试其中一个的时候,其他依赖伪造mock
* 本质还是测试方法(官网列出了两种测试方法,我们选择简单易懂便于测试的)
*/
import { mutations, getters, actions } from '@/store.js'
describe('store.js', () => {
// 测试内容:mutations
// 伪造(mock)state 测试mutations下的方法
it('mutations test', () => {
const state = {
count:0
}
mutations.increment(state)
expect(state.count).toBe(1)
mutations.decrement(state)
expect(state.count).toBe(0)
})
// 测试内容:getters
// 伪造(mock)state 测试evenOrOdd的值
it('getters test even', () => {
const state = {
count:0
}
expect(getters.evenOrOdd(state)).toBe('even')
})
// 测试内容:getters
// 伪造(mock)state 测试evenOrOdd的值
it('getters test odd', () => {
const state = {
count:1
}
expect(getters.evenOrOdd(state)).toBe('odd')
})
// 测试内容:actions 比前两个测试要稍微复杂
// 伪造(mock)commit 测试mutations下的方法
it('actions test', () => {
// 伪造commit
const commit = jest.fn()
// increment
actions.increment({ commit })
expect(commit).toBeCalled()
expect(commit).toBeCalledWith('increment')
// decrement
actions.decrement({ commit })
expect(commit).toBeCalled()
expect(commit).toBeCalledWith('decrement')
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
← VUE踩坑总结 VUE原生APP制作 →