[Day19] Vue 3 单元测试 (Unit Testing) - Event Handling

Event Handling

在开发元件时一定少不了会需要触发事件的时候,像是 click 事件、input 事件等等,所以我来用简单的 Counter 来示范要怎麽为事件攥写单元测试。

import { ref } from 'vue'

const Component = {
  template: `
    <div>
      <button data-test="button" @click="increment">Increment</button>
      <p data-test="count">{{count}}</p>
    </div>
  `,
  setup () {
    const count = ref(0)
    const increment = () => {
      count.value += 1
    }

    return {
      count,
      increment
    }
  }
}

在元件中有一个挂载 click 事件的 button 以及一个会显示目前 count 数值的 p。

要测试这个元件是否正常,我们应该验证三种情况是否正确运行:

  • Case 1: button 应该要存在。
  • Case 2: count 的初始值为 0。
  • Case 3: click 一次 button 後,count 的值变成 1。

Case 1 & Case 2。

// Case 1: button 应该要存在。 
test('render button', () => {
  const wrapper = mount(Component)

  expect(wrapper.get('[data-test="button"]').exists()).toBe(true)
})

// Case 2: count 的初始值为 0。
test('The initial value of count is 0', () => {
  const wrapper = mount(Component)

  expect(wrapper.get('[data-test="count"]').text()).toBe('0')
})

因为 case 1 和 case 2 使用的语法都是我们昨天有介绍的,所以我就直接放上程序码不多做解释,如果看不太懂的朋友可以赶快去看昨天的文章 Vue 3 单元测试 (Unit Testing) - Conditional rendering & Elements visibility

Case 3: click 一次 button 後,count 的值变成 1。

test('after click, count will be 1', async () => {
  const wrapper = mount(Component)

  await wrapper.get('[data-test="button"]').trigger('click')

  expect(wrapper.get('[data-test="count"]').text()).toBe('1')
})

语法说明:

  • trigger(): trigger 可以用来触发 DOM 事件,例如 click、submit 或 keyup 等操作,值得注意的是, trigger 回传的是一个 Promise,也因此我使用了 async & await 的方式来等待 promise resolve。

Emitted Events

除了浏览器定义的事件类型,如 click、submit 或 keyup 以外,在 Vue 中我们也可以利用 emits 来定义客制化的事件类型,这类型的事件通常是由子元件向父元件所触发的。

import { ref } from 'vue'

const Component = {
  template: `
    <div>
      <button data-test="button" @click="increment">Increment</button>
    </div>
  `,
  emits: ['increment'],
  setup (props, context) {
    const count = ref(0)
    const increment = () => {
      count.value += 1
      context.emit('increment', count.value)
    }

    return {
      count,
      increment
    }
  }
}

在元件中有一个挂载 click 事件的 button,click 後会将 count 的值加 1 并且 emit 出去。

要测试这个元件是否正常,我们应该验证四种情况是否正确运行:

  • Case 1: button 应该要存在。
  • Case 2: count 的初始值为 0。
  • Case 3: click 後触发 emit('increment') 事件。
  • Case 4: emit('increment') 将最新的 count 值抛出。

case 1 我们一样跳过不多做说明,不过 case 2 在这边就有点不一样了,因为现在 template 中少了呈现 count 的元素,所以底下会马上介绍要如何应对!

Case 2: count 的初始值为 0。

test('The initial value of count is 0', async () => {
  const wrapper = mount(Component)

  expect(wrapper.vm.count).toBe(0)
})

语法说明:

  • vm: vm 是 VueWrapper 的一个属性,我们可以透过 vm 来取得 Vue instance,如此一来就能再透过 vm 取得 count 变数了。

Case 3: click 後触发 emit('increment') 事件。

test('emits an event when clicked', async () => {
  const wrapper = mount(Component)

  await wrapper.get('[data-test="button"]').trigger('click')

  expect(wrapper.emitted()).toHaveProperty('increment')
})

语法说明:

  • emitted() : emitted() 会回传一个纪录元件发出的所有事件的物件,其中也包含着 emit 的参数。
  • toHaveProperty(): jest 有提供一个 toHaveProperty 的匹配器 (matcher),可以用来检查物件中是否存在某属性。

我稍微再解释一下 emitted() 的回传值,以上面为例子, 回传的内容会长得像这样。

{
  increment: [ [ 1 ] ],
  click: [ [ MouseEvent ] ]
}

物件中的 key (increment 和 click) 是触发的事件名称,value 则是事件的参数,然而因为同一个事件可能会触发多次,因此是用二维阵列来储存,第一层的阵列代表着不同次事件的参数,第二层的阵列代表着同一次事件的不同参数,所以如果 increment 触发两次的话,它的回传值会长的像。

{
  increment: [ [ 1 ], [2] ]
}

Case 4: emit('increment') 将最新的 count 值抛出。

test('after clicked, it will emit value 1', async () => {
  const wrapper = mount(Component)

  await wrapper.get('[data-test="button"]').trigger('click')

  const incrementEvent = wrapper.emitted('increment')

  expect(incrementEvent[0]).toEqual([1])
})

语法说明:

  • toEqual(): toEqual() 匹配器会去比较物件的所有属性或阵列的所有元素是否相等。

参考资料


今天的分享就到这边,如果大家对我分享的内容有兴趣欢迎点击追踪 & 订阅系列文章,如果对内容有任何疑问,或是文章内容有错误,都非常欢迎留言讨论或指教的!

明天要来分享的是 Vu3 单元测试 (Unit Testing) 主题的第五篇 Form Elements Handling ,那我们明天见!


<<:  Angular 客制弹出视窗

>>:  Day 19 - Integer to Roman

卡夫卡的藏书阁【Book26】- Kafka - KafkaJS Admin 3

“You are at once both the quiet and the confusion...

Day 9 - 目前(传统)的机器学习三步骤(4)-训练之测试

交叉验证 Cross-Validation (wiki) 交叉验证,有时亦称循环估计,是一种统计学上...

tiktok 网页版匿名浏览器

https://hiovid.com 是一款在线 tikok 浏览器,支援用户名搜索。可以轻松匿名浏...

Day 27 : Python - 什麽是列表推导式?又该如何将它和if、if-else一起做使用?

如标题,这篇想和大家聊聊「列表推导式」是什麽东西 我们先看看范例再说明,这样大家会比较好理解 Ex ...

24. 工程师之伤 x 久坐 x 徒手治疗

本篇是病人的非专业心得分享。 酸痛其实是老毛病,因为工程师一不小心就久坐,长期会让酸痛恶化。 如果...