一、前言       

        我们在vue项目中Vue Test Utils 测试环境已经搭建完成的基础上来进行演示,搭建环境可以看我之前的文章。       

        本文中使用的相关技术点连接:

        测试环境:  Vue Test Utils

        断言库:chai

       

二、基础

        1、describe:

                存放测试用例的容器,一个describe中可以定义多个测试用例。

               语法

          

describe('说明', () => {
                        // 函数中写测试用例
               })

        2、it:

                编写测试用例

                语法

              

it('描述', () => {
                                    //函数中编写断言(对结果的预期)
                          })

        3、运行

                1 package.json中的scripts对象中配置"unit": "jest --config test/unit/jest.config.js",

                2 项目 跟目录运行npm run unit

三、

        1、第一个测试用例演示 创建测试文件 test.spec.js

describe('测试', () => {
  it('测试用例', () => {
    // 演示一下,断言字符串'hello'内容是'hello',当然我这么写,结果肯定是true的。
    // epect后面的字符串以后会变成真正的页面内容。
    expect('hello').toContain('hello')
  })
})

                运行npm run unit

Vue用jest进行单元测试 vue test utils_html

                我们看到结果显示已经通过测试

        2、挂载和测试

                创建counter.vue

// counter.vue
<template>
  <div>
    <span class="count">{{ count }}</span>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },

  methods: {
    increment() {
      this.count++
    }
  }
}

</script>

<style>

</style>

                 编写测试文件counter.spec.js

import { mount } from '@vue/test-utils'
import Counter from '@/components/counter'

describe('测试counter组件', () => {
  // 现在挂载组件,你便得到了这个包裹器
  const wrapper = mount(Counter)

  it('测试是否渲染了<span class="count">0</span>', () => {
    expect(wrapper.html()).toContain('<span class="count">0</span>')
  })

  // 也便于检查已存在的元素
  it('测试是否存在一个按钮', () => {
    expect(wrapper.find('button').exists()).toBe(true)
  })

  // 模拟用户交互
  it('单击按钮将增加计数', () => {
    // 测试组件中count的初始值是0
    expect(wrapper.vm.count).toBe(0)
    const button = wrapper.find('button')
    // 触发按钮点击事件
    button.trigger('click')
    // 点击累加按钮后 count的值应该变为1
    expect(wrapper.vm.count).toBe(1)

  })

  // 页面dom解构发生变化以后需要等页面更新后才能拿到内容。
  it('单击按钮将增加计数文本', async () => {
    // 此处count已经不是0了,上面已经点击了一次,此时变为1
    expect(wrapper.text()).toContain('1')
    const button = wrapper.find('button')
    await button.trigger('click')
    expect(wrapper.text()).toContain('2')
  })

    // 断言触发方法
    it('断言触发increment方法后的结果', async () => {
      expect(wrapper.text()).toContain('2')
      wrapper.vm.increment()
      // 触发事件后断言页面信息
      await wrapper.vm.$nextTick()
      expect(wrapper.text()).toContain('3')
      await wrapper.vm.$nextTick()
      expect(wrapper.text()).toContain('3')
    })

  // 断言触发的事件
  it('断言触发事件后的结果', async () => {
    wrapper.vm.$emit('foo')
    // 断言事件已经被触发
    expect(wrapper.emitted().foo). toBeTruthy()
    
  })


  
  // 组件不会自动注销,我们需要手动注销
  // wrapper.destroy()
  
})

        3、父子组件

                创建子组件ChildComponent.vue

<template>
  
</template>

<script>
export default {

}
</script>

<style>

</style>

                创建父组件ParentComponent.vue

<template>
  <div>
    <child-component @custom="onCustom" />
    <p v-if="emitted">Emitted!</p>
    <div>
      <p>{{msg}}</p>
      <p>{{title}}</p>
      <p>{{aProp}}</p>
    </div>
  </div>
</template>

<script>
  import ChildComponent from './ChildComponent'

  export default {
    name: 'ParentComponent',
    components: { ChildComponent },
    data() {
      return {
        emitted: false,
        msg: ''
      }
    },
    props: ['title', 'aProp'],
    methods: {
      onCustom() {
        this.emitted = true
      }
    }
  }
</script>

                创建测试文件childComponent.spec.js

import { mount } from '@vue/test-utils'
import ParentComponent from '@/components/ParentComponent'
import ChildComponent from '@/components/ChildComponent'

// 从子组件触发事件
describe('ParentComponent', () => {
  
  it("displays 'Emitted!' when custom event is emitted", async () => {
    // 使用包裹器载入载入渲染父组件,因为是深度渲染,所以父组件中的子组件会被渲染。
    const wrapper = mount(ParentComponent)
    // 在父组件中找到子组件,并且在子组件中触发自定义事件custom。
    // 此时父组件中的子组件上custom事件注册的函数onCustom会被执行。
    // 官网使用的是老版本find,运行后会报错语法更新
    // wrapper.find(ChildComponent).vm.$emit('custom')
    // 我们使用新语法findComponent
    wrapper.findComponent(ChildComponent).vm.$emit('custom')
    // 验证父组件中html中是否有'Emitted!'
    // 此处页面dom内容发生更新需要延迟验证。
    // 此处官网直接断言 expect(wrapper.html()).toContain('Emitted!'),结果当然是false。
    // 此处页面dom内容发生更新需要延迟验证。
    await wrapper.vm.$nextTick()
    expect(wrapper.html()).toContain('Emitted!')
  })

  // 在测试用例中操作组件的状态
  // 可以在包裹器上用 setData 或 setProps 方法直接操作组件状态:
  // 在父组件中,data中增加msg,props中增加title。在dom中渲染插值表达式渲染这两项。
  it('操作组件状态', async () => {
    const wrapper = mount(ParentComponent)
    await wrapper.setData({msg: 'hello jset'})
    expect(wrapper.html()).toContain('hello jset')
    await wrapper.setProps({ title: 'my title' })
    expect(wrapper.html()).toContain('my title')
  })

  // 仿造 Prop
  // 可以使用 Vue 在内置 propsData 选项向组件传入 prop:
  it('伪造prop传给子组件', async () => {
    const wrapper = mount(ParentComponent, {
      propsData: {
        aProp: 'some value'
      }
    })
    expect(wrapper.html()).toContain('some value')
  })


})

四、代码覆盖率

             控制台会输出代码覆盖率:

Vue用jest进行单元测试 vue test utils_测试用例_02

          通过分析文件查看代码覆盖率:

                找到当前项目下test/unit/coverage/lcov-report/index.html,并使用浏览器运行。

Vue用jest进行单元测试 vue test utils_html_03

%stmts是语句覆盖率(statement coverage):是不是每个语句都执行了?
%Branch分支覆盖率(branch coverage):是不是每个if代码块都执行了?
%Funcs函数覆盖率(function coverage):是不是每个函数都调用了?
%Lines行覆盖率(line coverage):是不是每一行都执行了?