基于vue项目的jest单元测试疑难点

451 阅读2分钟

最近新入职了一家公司,做国外的项目,使用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里面的方法,然后获取相应的返回值