试着为vue项目添加单元测试(一)__Vue.js
发布于 4 年前 作者 banyungong 1834 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

简单的事情往往被忽略,说到单元测试,很多团队都只是在接口部分加入,UI层面最容易被忽略。今天我将围绕一个简单的demo写一下ui层面的单元测试。该文章仅仅面对没接触UI层面单元测试,且想快速上手的人,大佬可将鼠标移到右上角点击x即可。

👓 前端做单元测试(what? why?)

  • 模拟用户操作,先于用户发现问题。

👇 需要提前了解什么知识?准备什么?

  • 像char、mocha、jest这个库倒是可以简单了解一下
  • 如果你想快速上手跟着我敲一遍代码也足够了
  • 在接下来的测试中,vuecli已经提供了@vue/test-utils库
  • 由于个人习惯,我另外安装了chai的断言库(ts项目需要另外安装@types/chai)

👏 先来看看我要测试的这个demo

非常简单,但是“基本能覆盖”一般ui层面的测试,考虑哪些会在我们测试范围内?

  • 点击“打开嵌套表单的Dialog”按钮,弹窗是否能弹出
  • 输入的值是否符合我们想要的类型(正常来说是在点击确定按钮之后去核验)
  • 点击确定按钮和取消按钮是否能关闭弹窗

😾 通过代码来看看测试的细节

  1. 需要测试的组件
// 需要测试的组件
<template>
  <div class="about">
    <el-button type="text" @click="dialogFormVisible = true">打开嵌套表单的 Dialog</el-button>
    <el-dialog title="收货地址" :visible.sync="dialogFormVisible">
      <el-form :model="form">
        <el-form-item label="姓名" :label-width="formLabelWidth">
          <el-input v-model="form.name" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="年龄" :label-width="formLabelWidth">
          <el-input v-model="form.age" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
@Component
export default class About extends Vue {
  private dialogFormVisible = false;
  private form = {
    name: '',
    age: 20,
  };
  private formLabelWidth = '120px';
}
</script>
  1. 先来看一下测试用例的具体代码,具体解释在代码中体现
import {mount, config, createLocalVue } from '[@vue](/user/vue)/test-utils';
import About from '@/views/About.vue';
import Vue from 'vue'
import ElementUI from 'element-ui'
import { expect } from 'chai'

// 引入elementui,不对全局的 Vue 构造函数注入任何东西。我们可以使用 createLocalVue 方法来存档它们:
const localVue = createLocalVue()
localVue.use(ElementUI)

config.stubs!.transition = false

describe('about.vue', () => {
  // 由于在接下来的几个测试中都会用到同一个容器,所以仅需将vue挂载一次就可以了
  const wrapper = mount(About, {
    localVue
  })
  // 默认我们将使用async,await模式
  it('dialog pop', async () => {
    const dialog = wrapper.find('.el-dialog__wrapper') // 拿到弹窗
    const openBtn = wrapper.findAll('.el-button').at(0) // 拿到触发弹窗显示的按钮

    expect(dialog.attributes().style).contain('display') // 因为是elementui内部组件本有特性,这步可省略
    expect(wrapper.vm.$data.dialogFormVisible).to.be.eq(false) // 控制弹窗的变量初始值为false
    await openBtn.trigger('click') // 触发按钮的单击事件
    expect(dialog.attributes().style).to.not.contain('display') // 同上可省略
    expect(wrapper.vm.$data.dialogFormVisible).to.be.eq(true) // 控制弹窗的变量值是否变为true
  });

  // 对值的检查
  it('check value', async () => {
    const openBtn = wrapper.findAll('.el-button').at(0)
    await openBtn.trigger('click')

	// 模拟输入值
    await wrapper.setData({
          form: {
            name: 'xiaomin',
            age: 30
          }
        })
    expect(typeof(wrapper.vm.$data.form.name)).to.be.eq('string') // 判断输入的值是否输入成功,是否为提前设置的类型
    expect(typeof(wrapper.vm.$data.form.age)).to.be.eq('number') // wrapper.vm.$data可拿到所有的变量
  })

  // 判断弹窗是否能正常关闭
  it('dialog close', async () => {
    const openBtn = wrapper.findAll('.el-button').at(0)
    await openBtn.trigger('click')

    expect(wrapper.vm.$data.dialogFormVisible).to.be.eq(true)

    const closeBtn = wrapper.findAll('.el-button').at(1) //根据dom位置,拿到取消按钮,并触发
    await closeBtn.trigger('click')
    expect(wrapper.vm.$data.dialogFormVisible).to.be.eq(false)
  })
});
  1. 让我们看看上面代码中这个wrapper的dom,方便更好的理解。 console.log(wrapper.html())

  1. 现在跑一下这个测试(npm run test:unit)

  2. 踩的两个坑!

Can’t find stylesheet to import

  • 相对路径改为绝对路径即可

TypeError: Cannot read property ‘$el’ of undefined"

  • 这是由Vue Test Utils默认添加的同步Transition存根组件引起的。 您可以使用存根选项将其关闭config.stubs!.transition = false

😼 总结

  • 无论是前端还是后端,对单元测试的书写,也是对自己代码的一次检查,所以强烈建议对单元测试的重视
  • 本文仅代码个人观点,若有写的不合适的地方,欢迎批评指正。

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 刘胖儿 原文链接:https://juejin.im/post/6856649733290377224

回到顶部