vue-test-util
jest.config.js
module.exports = {
transform: {
'^.+\\.vue$': 'vue-test',
'^.+\\js$': 'babel-test',
'^.+\\.(t|j)sx?$': 'ts-jest',
},
setupFiles: ['<rootDir>/src/jest-setup.js']
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
resolver: null,
transformIgnorePatterns: ['/!node_modules\\/lodash-es/']
moduleFileExtensions: ['vue', 'js', 'json', 'jsx', 'ts', 'tsx', 'node']
}
jest-setup.js
import * as vue from 'vue'
for(const key of Object.keys(vue)) {
global[key] = vue[key]
}
语法
- shallowMount和mount
- shallowMount只渲染当前的组件
- mount渲染组件以及子组件
- Wrapper, 包含了一个挂载组件或者vnode,以及测试组件和vnode组件的方法
- .vm 组件vue实例
- .classes(‘test’) 返回是否拥有该class的dom或者类名数组
- .find() 返回第一个满足条件的dom
- .findAll() 返回所有满足条件的dom
- .html() 返回html字符串
- .text() 返回内容字符串
- .setData() 设置组件data
- .setProps() 设置props数据
- .trigger() 组件触发事件
- .emitted() 验证事件触发
- jest断言
- toBeNull
- toBeUndefined
- toBeDefined
- toBeNaN
- toBeTruthy
- toBeFalsy
- toContain 数组用,检测是否包含
- toHaveLength 数组用,检测数组长度
- toHaveProperty 数组用,检测对象是否包含这个key
- toEqual 对象用,检测是否相等
- toThrow 异常匹配
代码的话,看官网就行https://v1.test-utils.vuejs.org/
代码
import Api from "@/api"
describe('@/api/index.ts', () => {
it('getUsers接口,不接受入参', () => {
return Api.getUsers().then((res: any) => {
expect(res.length).toBe(10)
})
})
it('getUser接口,接受入参', async () => {
expect(res.length).toBe(1)
expect(res[0].id).toBe(1)
});
});
jest.mock('@/api/request', () => {
GET: () => Promise.resolve([{username: 'Xyz'}])
})
import Api from '@/api'
xdescribe('@/api/index.ts', () => {
it('getUser接口,不接受入参', () => {
return Api.getUsers().then((res: any) => {
expect(res.length).toBe(1)
expect(res[0].username).toMatch('Xyz')
})
})
})
import { mount, createLocalVue} from "@vue/test-utils";
import Counter from "@/components/Counter.vue";
import { Button} from "element-ui";
const localVue = createLocalVue()
localVue.component(Button.name, Button)
const wrapper = mount(Counter, { localVue })
describe('Counter.vue', () => {
it('默认状态值为0', () => {
expect(wrapper.vm.$data.count).toBe(0)
});
it (' 修改count值为20 ' , () => {
wrapper.setData({ count: 20})
expect(wrapper.find('span').text()).toMatch('20')
});
it('由于子组件出发click事件,更行count为22', () => {
wrapper.setData({count: 21})
wrapper.find(Button).vm.$emit('click')
expect(wrapper.vm.$data.count).toBe(22)
});
it('由子组件内的button出发点击事件,更新为count为11', () => {
wrapper.setData({count: 10})
const elBtn = wrapper.get(Button)
elBtn.find('button').trigger('click')
expect(wrapper.vm.$data.count).toBe(11)
});
});
import { mount, createLocalVue } from '@vue/test-utils'
import Counter2 from "@/components/Counter2.vue"
import { Button } from 'element-ui'
const localVue = createLocalVue()
localVue.component(Button.name, Button)
describe("Counter2.vue", () => {
it('默认prop值为10', () => {
const wrapper = mount(Counter2, {localVue})
expect(wrapper.props().count).toBe(10)
expect(wrapper.props('count')).toBe(10)
expect(wrapper.find('span').text()).toMatch('10')
})
it('修改props.count的值为12', () => {
const wrapper = mount(Counter2, {localVue, propsData: {count:11}})
expect(wrapper.props().count).toBe(11)
expect(wrapper.props('count')).toBe(11)
expect(wrapper.find('span').text()).toMatch('11')
})
it('修改props.count的值为12', () => {
const wrapper = mount(Counter2, {localVue})
wrapper.setProps({count: 12})
expect(wrapper.props().count).toBe(12)
expect(wrapper.props('count')).toBe(12)
expect(wrapper.find('span').text()).toMatch('12')
})
it('连续点击,update方法被调用2次', () => {
const wrapper = mount(Counter2, {localVue})
const update = jest.fn()
wrapper.vm.$on('update', update)
wrapper.find(Button).vm.$emit('click')
expect(update).toBeCalled()
expect(update).toBeCalledTimes(1)
expect(update).toBeCalledWith(11)
wrapper.setProps({count: 12})
wrapper.find(Button).vm.$emit('click')
expect(update).toBeCalledTimes(2)
expect(update).toBeCalledWith(12)
})
})
import { mount } from '@vue/test-utils'
import Vue from 'vue'
import FetchData from '@/components/FetchData.vue'
jest.mock('@/utils', () => ({
getRandomNum: () => 6
}))
jest.mock('@/api', () => ({
getUsers: () => Promise.resolve([{username: '~!@#$%^&*()_+'}])
}))
describe('FetchData.vue', () => {
it('在created生命周期中调用方法', () => {
const getUser = jest.fn()
const options = {
methods: {getUser}
}
mount(FetchData, options)
expect(getUser).toBeCalled()
})
it("保存异步接口的返回值", () => {
const wrapper = mount(FetchData)
return Vue.nextTick().then(() => {
expect(wrapper.vm.$data.msg).toMatch('~!@#$%^&*()_+')
})
})
})
import { createLocalVue, shallowMount, RouterLinkStub, mount } from "@vue/test-utils";
import VueRouter, { RouterView } from "vue-router";
import App from '@/App.vue'
import ElementUI from "element-ui";
import routes from '@/router/routes'
const localVue = createLocalVue()
localVue.use(VueRouter)
localVue.use(ElementUI)
describe('App.vue', () => {
it('非函数式组件', () => {
const wrapper = shallowMount(App, {
localVue
})
expect(wrapper.vm).toBeTruthy()
})
it('有三个路由', () => {
const wrapper = shallowMount(App, {
localVue,
stubs: {'router-like': RouterLinkStub, 'router-view': '<span />'}
})
expect(wrapper.findAll(RouterLinkStub).length).toBe(3)
})
it('第三个路由为/page', () => {
const wrapper = shallowMount(App, {
localVue,
stubs: {RouterLink: RouterLinkStub, RouterView: true}
})
expect(wrapper.findAll(RouterLinkStub).at(2).props().to).toMatch('/page')
})
it('路由到About页面', ( done ) => {
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
const wrapper = mount(App, {
localVue,
router
})
router.push('/about').then(() => {
expect(wrapper.currentRoute.name).toMatch('/about')
done()
})
})
it('路由到pageA页面', () => {
const router = new VueRouter({
mode: 'history',
base: process.env.Base_URL,
routes
})
const wrapper = mount(App, {
localVue,
router
})
})
})