🍀封装个 Button 组件,使用 vitest 来测试一下

Button 封装

vue 复制代码
<template>
  <button
    ref="_ref"
    class="h-button"
    :class="{
      [`h-button--${type}`]: type,
      [`h-button--${size}`]: size,
      'is-plain': plain,
      'is-round': round,
      'is-circle': circle,
      'is-disabled': disabled,
      'is-loading': loading
    }"
    :disabled="disabled || loading"
    :autofocus="autofocus"
    type="button"
  >
    <!-- 加载状态 -->
    <Icon icon="spinner" spin v-if="loading" />
    <!-- 图标 -->
    <Icon :icon="icon" v-if="icon" />
    <span>
      <slot />
    </span>
  </button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ButtonProps } from './types'

defineOptions({
  name: 'HButton'
})

const _ref = ref<HTMLButtonElement>()

defineExpose({
  ref: _ref
})
</script>

<style lang="less" scoped></style>

Button 类型定义

typescript 复制代码
export type ButtonType = 'primary'| 'success'| 'warning'| 'danger'| 'info'
export type ButtonSize = 'large' | 'small'

export interface ButtonProps {
  type?: ButtonType;
  size?: ButtonSize;
  plain?: boolean;
  round?: boolean;
  circle?: boolean;
  disabled?: boolean;
  autofocus?: boolean;
  icon?: string;
  loading?: boolean;
}

Button 入口文件

typescript 复制代码
import type { App } from 'vue'
import Button from './Button.vue'

Button.install = (app: App) => {
  app.component(Button.name, Button)
}

export default Button

export * from './types'

使用 Vitest 对 Button 进行单元测试

typescript 复制代码
/**
 * describe 是 vitest 中的一个函数,用于描述一组测试用例。
 * test 是 vitest 中的一个函数,用于定义一个测试用例。
 * expect 是 vitest 中的一个函数,用于断言测试结果。
 * mount 是 vue-test-utils 中的一个函数,用于挂载组件。
 */
 
 describe('Button.vue', () => {
   // 测试一
   test('test1', () => { /* -------- */})
   
   // 测试一
   test('test2', () => { /* -------- */})
 })

基本功能测试

typescript 复制代码
test('test1', () => {
    // 生成一个 Button 组件的实例
    const wrapper = mount(Button as any, {
      props: {
        type: 'primary'
      },
      // 组件的插槽
      slots: {
        // 插入文本 button test
        default: 'button test'
      }
    })

    // wrapper.classes() 获取组件的 class 列表;toContain 判断 class 列表中是否包含指定的 class
    expect(wrapper.classes()).toContain('h-button--primary')

    // wrapper.get('button') 获取组件中的 button 元素
    // wrapper.get('button').text() 获取 button 元素的文本内容; toBe 判断文本内容是否等于指定的文本内容
    expect(wrapper.get('button').text()).toBe('button test')

    // trigger('click') 触发 button 元素的 click 事件
    wrapper.get('button').trigger('click')

    // toHaveProperty 判断事件列表中是否包含指定的事件;
    expect(wrapper.emitted()).toHaveProperty('click')
})

TIP

  • mount:原来模拟页面渲染组件
  • getfind 都是查找组件中是否有某个原生dom元素 ;区别于 findComponent
  • wrapper.emitted():获取组件触发的事件列表
  • wrapper.html(): 获取组件的 html 字符串

属性验证测试(禁止点击为例)

typescript 复制代码
test('test2', () => {
    const wrapper = mount(Button as any, {
      props: {
        disabled: true
      },
      slots: {
        default: 'disabled'
      }
    })
    
    // * wrapper.attributes() 获取组件的属性列表;
    // * toBeDefined 判断属性列表中是否包含指定的属性;
    expect(wrapper.attributes('disabled')).toBeDefined()


    // 判断 button 元素是否禁用;
    expect(wrapper.find('button').element.disabled).toBeDefined()

    // 触发点击事件;
    wrapper.get('button').trigger('click')
    // 事件列表没有 click 事件;
    expect(wrapper.emitted()).not.toHaveProperty('click')
})

Tip

  • wrapper.attributes('disabled'):获取组件 disabled 属性
  • wrapper.find('button').element.disabled:原生 button 是否被禁止了

加载状态测试

typescript 复制代码
test('test4', () => {
    const wrapper = mount(Button as any, {
      props: {
        loading: true
      },
      slots: {
        default: 'loading'
      },
      global: {
        stubs: ['Icon']
      }
    })

    // 查找组件中的 Icon 组件
    const iconElement = wrapper.findComponent(Icon)
    expect(iconElement.exists()).toBeTruthy()
    expect(iconElement.attributes('icon')).toBe('spinner')

    // 组件是否禁用
    expect(wrapper.attributes('disabled')).toBeDefined()
})

组件引用测试

typescript 复制代码
  test('test3', () => {
    const wrapper = mount(Button as any, {
      props: {
        // 传入图标名称
        icon: 'arrow-up'
      },
      slots: {
        default: 'icon'
      },

      global: {
        // 创建一个虚拟的 FontAwesomeIcon 组件
        stubs: ['FontAwesomeIcon']
      }
    })

    // 查找组件中的 FontAwesomeIcon 组件 (icon 组件内部使用了 FontAwesomeIcon)
    const iconElement = wrapper.findComponent(FontAwesomeIcon)

    // 断言组件是否存在
    expect(iconElement.exists()).toBeTruthy()
    // 断言组件的属性是否正确
    expect(iconElement.attributes('icon')).toBe('arrow-up')
  })
相关推荐
Hi_kenyon6 小时前
VUE3套用组件库快速开发(以Element Plus为例)二
开发语言·前端·javascript·vue.js
起名时在学Aiifox6 小时前
Vue 3 响应式缓存策略:从页面状态追踪到智能数据管理
前端·vue.js·缓存
李剑一7 小时前
uni-app实现本地MQTT连接
前端·trae
EndingCoder7 小时前
Any、Unknown 和 Void:特殊类型的用法
前端·javascript·typescript
oden7 小时前
代码高亮、数学公式、流程图... Astro 博客进阶全指南
前端
GIS之路7 小时前
GDAL 实现空间分析
前端
JosieBook8 小时前
【Vue】09 Vue技术——JavaScript 数据代理的实现与应用
前端·javascript·vue.js
pusheng20258 小时前
算力时代的隐形防线:数据中心氢气安全挑战与技术突破
前端·安全
起名时在学Aiifox8 小时前
前端文件下载功能深度解析:从基础实现到企业级方案
前端·vue.js·typescript