🍀封装个 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')
  })
相关推荐
wenzhangli76 小时前
Ooder A2UI 核心架构深度解析:WEB 拦截层的设计与实现
前端·架构
前端百草阁6 小时前
【前端性能优化全链路指南】从开发编写到构建运行的多维度实践
前端·性能优化
女生也可以敲代码7 小时前
AI时代下的50道前端开发面试题:从基础到大模型应用
前端·面试
ZhengEnCi7 小时前
M5-markconv自定义CSS样式指南 📝
前端·css·python
IT_陈寒7 小时前
SpringBoot自动配置的坑差点让我加班到天亮
前端·人工智能·后端
xingpanvip7 小时前
星盘接口开发文档:星相日历接口指南
android·开发语言·前端·css·php·lua
@PHARAOH7 小时前
WHAT - GitLens supercharged 插件
前端
TT模板8 小时前
苹果cms整合西瓜播放器XGplayer插件支持跳过片头尾
前端·html5
Wect8 小时前
React 性能优化精讲
前端·react.js·性能优化
追风筝的人er9 小时前
SpringBoot+Vue3 企业考勤如何处理法定假期?节假日方案、调休补班与工作日判断链路拆解
前端·vue.js·后端