测试事件

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-02-26

测试原生DOM事件

Vue Test Utils 中,每个包装器都有一个 trigger 方法,用于在包装元素上分发一个合成事件。

合成事件是 JavaScript 中创建的事件。实际上,合成事件的处理方式与浏览器分发事件的方式相同。区别在于原生事件通过 JavaScript 事件循环异步调用事件处理程序,合成事件则同步调用事件处理程序。

trigger 使用一个 eventType参数创建包装器上分发的事件。

示例:测试按钮被点击时 onClose prop 是否被调用。

test('calls onClose when button is clicked', () => {
  const onClose = jest.fn();
  const wrapper = shallowMount(Modal, { // 浅挂载一个Modal组件传递一个props
    propsData: { onClose }
  });

  warpper.find('button').trigger('click');
  expect(onClose).toHaveBeenCalled();
})

对应的 Modal 组件如下,父组件在调用时会传一个函数过来 <Modal :onClose="parentCloseEvent"/>

<template>
  <div>
    <button @click="onClose" />
    <slot />
  </div>
</template>

<script>
export default {
  props: ['onClose']
}
</script>

测试自定义Vue事件

1、测试触发:测试自定义事件是否被触发

Vue Test Utilsemitted 方法测试组件是否发射事件,使用事件名称调用 emitted 以返回一个数组,该数组包含了每个发射事件的 payload; 你也可以使用emitted 来测试一个事件是否以正确的顺序或使用正确的数据被调用。

this.$emit('test-emit', paylaod1);
this.$emit('test-emit', paylaod2);

wrapper.emitted('test-emit') //  [payload1, payload2]

示例:在按钮上触发 click 事件,断言组件实例是否使用 emitted 方法触发了一个 close-modal 事件;

test('emits on-close when button is clicked', () => {
  const wrapper = shallowMount(Modal);
  warpper.find('button').trigger('click');
  expect(warpper.emitted('close-modal')).toHaveLength(1); // 断言close-modal被处罚了一次
})
<template>
  <div>
    <button @click="$emit('close-modal')" />
    <slot />
  </div>
</template>

2、测试监听:测试在触发自定义事件后,被测组件的响应是否是正确的

示例:测试当你从 Modal 事件发射一个 close-modal 事件时,App 组件是否正确响应了。

所以我们需要测试的 App 组件,只需要引入 Modal 组件然后主动触发事件(不需要挂载),然后测试 App 的响应是否正确。

import App from '../../App.vue'
import { shallowMount } from '@vue/test-utils'
import Modal from '../../components/Modal.vue'

describe('App.vue', () => {
  test('hides Modal when Modal emits close-modal', async () => {
    const wrapper = shallowMount(App)
    wrapper.findComponent(Modal).vm.$emit('close-modal')
    expect(wrapper.findComponent(Modal).exists()).toBeFalsy()// 是否正常响应
  })

  test('App.closeModal called when Modal emits close-modal', async () => {
    const closeModal = jest.fn();
    const wrapper = shallowMount(App, {
      methods: {
        closeModal
      },
    })
    wrapper.findComponent(Modal).vm.$emit('close-modal')
    expect(closeModal).toHaveBeenCalled() // 对应的方法是否被处罚
  })
})
<!-- App.vue -->
<template>
  <Modal
    v-if="displayModal"
    @close-modal="closeModal"
  />
</template>
<script>
import Modal from './components/Modal'

export default {
  name: 'App',
  data: () => ({
    displayModal: true
  }),
  components: {
    Modal
  },
  methods: {
    closeModal () {
      this.displayModal = false
    }
  }
}
</script>

测试输入表单

输入表单可以包含许多逻辑来处理验证并使用输入值执行操作,所以这些逻辑是需要被测试的。

1、测试文本输入框

const wrapper = shallowMount(Form);
const input = wrapper.find('input[type="email"]');
input.setValue('email@gmail.com');

2、测试触发提交表单事件后,是否使用了正确的参数调用了POST;

test('send post request with email on submit', () => {
  const axios = {
    post: jest.fn()
  }

  const wrapper = shallowMount(Form, {
    mocks: { axios }
  })

  const input = wrapper.find('input[type="email"]');
  input.setValue('email@gmail.com');
  wrapper.find('button').trigger('submit');
  const url = 'http://demo.test.io/validate';

  const expectData = expect.objectContaining({
    email: 'email@gmail.com',
  })

  expect(axios.post).toHaveBeenCalledWith(url, expectData);

  // 这一段使用 objectContaining
  // 是因为你此时可能只想测试是否带了正确的参数 email
  // 但是可能 axios的参数有多个 objectContaining 指包含就可以
  // 如果直接写,则要求是 '全等于'

  // 不使用 objectContaining
  // expect(axios.post).toHaveBeenCalledWith(url, {
  //   email: 'email@gmail.com',
  // })
})

3、测试单选按钮

const wrapper = shallowMount(Form);
const radioInput = wrapper.find('input[type="radio"]');
radioInput.setChecked();

// 找到对应的单选按钮点击
const radioNoInput = wrapper.find('input[value="no"]');
radioNoInput.setChecked();

// 普通写法
// radioInput.element.checked = true;

了解 jsdom 的局限性

要在 Node 中运行 Vue 单元测试,你需要使用 jsdom 模拟 DOM 环境。多数情况下,这是有效的,但有时你也会遇到未实现特性的问题。

在 jsdom 中,web平台未实现的两大部分是:

  • 布局:关于计算元素位置的,如 Element.getBoundingRects这样的 DOM 方法将不会按预期执行,你如果使用元素的位置来计算组件的样式就会遇到类似的问题;

  • 导航:jsdom 中没有页面的概念,因此你无法创建请求并导航到其他页面上;

本章例子中,表单中的submit 事件的行为与它在浏览器中的行为不一样,在浏览器中表单的 submit 事件会在默认情况下创建一个 GET 请求,从而使页面重新加载,但是我们不需要这种行为,因此你需要编写代码防止事件发送 GET 请求导致页面重新加载;

<form name="email-form" @submit.prevent="onSubmit">

也正因为此,如果需要检查一个表单的提交动作是否会导致页面重新加载,你需要编写的是端到端测试来补充。

jsdom 中未实现的两个部分,布局和导航;了解这些限制很重要,因为当你遇到这些限制的时候,你不该做模拟,而是应该使用端到端测试补充你的单元测试。