最近新入职了一家公司,做国外的项目,使用vue+nuxt开发,但是要求做单元测试。网上的资料甚少,在实际写单元测试过程中也是磕磕碰碰,项目已经做完了,将项目中用到的单元测试操作总结如下。
项目使用的是jest做单元测试,主要是对项目中使用到的方法,函数,页面UI部分等进行测试。对jest如何安装使用,如何配置,不在本文章之列。
写在前面
使用jest做单元测试,有几个重要的语法:
toBe——测试是否匹配
toBeTruthy——匹配任何if语句为真
toHaveBeenCalled ——测试方法是否被执行
toHaveBeenCalledTimes——测试方法被执行次数
toHaveBeenCalledWith——测试方法的参数
jest.mock()——mock函数
1、测试路由跳转
vue项目代码:
login(){
this.$router.push("/")
}
单元测试:
it('check login methods and will call router.push', async () => {
const mockRouter = {
push: jest.fn()
}
const wrapper = shallowMount(login, {
mocks: {
$router: mockRouter//需要mock掉原来的路由跳转
},
localVue
})
await wrapper.vm.login()//执行login.vue页面的login事件
expect(mockRouter.push).toHaveBeenCalledTimes(1)//跳转路由执行一次
expect(mockRouter.push).toHaveBeenCalledWith('/')//路由跳转的参数
})
2、事件是否触发
执行一个事件后,是否调用了另一个事件
vue项目代码:
search(){
this.getData()//执行search方法,调用getData方法
}
单元测试:
it('search method,then getData render', async ()=>{
const getDataSpy = jest.spy(search.methods,'getData')
const wrapper=shallowMount(search)
await wrapper.vm.search()
expect(getDataSpy).toHaveBeenCalled()
})
3、测试elementUI的$refs事件
vue项目代码:
async close() {
this.dialogShow = false
if (this.$refs.form) {
this.$refs.form.resetFields()
}
},
单元测试:
it('check $refs',async ()=>{
const wrapper = shallowMount(login, {
localVue
})
wrapper.vm.$refs.firstNewPwd.resetFields = jest.fn().mockReturnValue(true)//设置$refs的返回值
wrapper.setData({dialogShow: true})//设置dialogShow初始值
await wrapper.vm.close()
expect(wrapper.vm.dialogShow).toBe(false)
expect(wrapper.vm.$refs.firstNewPwd.resetFields).toHaveBeenCalledTimes(1)
})
4、测试表单校验,this.$refs.form.valide
vue项目代码:
save() {
this.$refs.form.validate(async (valid) => {
if (valid) {
...
this.$router.push('/')
} else {
return false
}
})
}
单元测试
it('check $refs', async () => {
const Form = {
render: jest.fn(),
methods: {
validate: cb => {
cb(true)
}
}
}
let mockRouter = {push:jest.fn()}
const wrapper = shallowMount(login, {
mocks:{
$router:mockRouter
},
localVue,
stubs: {'el-form': Form}//使用stubs,返回true,跳过这个form表单的校验
})
await wrapper.vm.save()
expect(mockRouter.push).toHaveBeenCalled()
expect(mockRouter.push).toHaveBeenCalledWith('/')
})
5、测试js文件中,方法是否执行
在js文件中,如果想测试方法执行,但这个时候又不能将方法挂载到vue实例上,比如写在store的action里面的方法,怎么测试呢?在写法上稍有区别
vue项目代码:
async getUser({mockAxios = null} = {}) {
const axios = mockAxios ?? this.$axios
return await axios.user.getUser()
}
首先我们需要对方法进行mock,但是这里的方法就只是方法,并没有挂载到具体的vue实例上,我们不能通过shallowMount/mount获取到vue实例,那么在测试这个方法的时候,我们需要给这个方法进行传参,如上:
单元测试:
it('check getStaffProfile method', async () => {
const mockAxios = {
user: {
getUser: jest.fn()
}
}
await actions.getUser({mockAxios})
expect(mockAxios.user.getUser).toHaveBeenCalledTimes(1)
})
6、测试样式,测试背景颜色
在使用jest做单元测试时,仅支持内联中的样式,无法识别vue的样式,外部样式。
在测试背景颜色时,也有一个坑需要注意,设置颜色时使用带有井号的颜色,测试时使用rgb形式且数值之间要有空格
it('check background', () => {
const wrapper = shallowMount(bg)
wrapper.setData({btnBackground: '#47CC74'})
const saveBtn = wrapper.findComponent('.btn')
expect(saveBtn.attributes().style).toContain(
'background: rgb(71, 204, 116)'
)
})
总结:
1、使用mock,其实是将项目中的this用单元测试中的实例代替。
比如这行代码:我在项目中对axios进行了封装,在请求后台接口的时候,直接使用this.$axios.user.getUser()这样的方法请求。其中user是js文件的名称,getUser()是user.js文件中的一个函数。
项目中使用:
let res = await this.$axios.user.getUser()
if(res.data){
...
}
在单元测试的时候,就讲this进行替换。可以这么写:
const wrapper = shallowMount(user,{
mocks:{
$axios:{
user:{
getUser:jest.fn(_=>({data:true}))
}
}
}
})
在执行方法的时候,单元测试中也会执行mocks里面的方法,然后获取相应的返回值