🍀封装个 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')
  })
相关推荐
蓝银草同学6 小时前
阿里 Iconfont 项目丢失?手把手教你将已引用的 SVG 图标下载到本地
前端·icon
布列瑟农的星空6 小时前
重学React —— React事件机制 vs 浏览器事件机制
前端
一小池勺7 小时前
CommonJS
前端·面试
孙牛牛7 小时前
实战分享:一招解决嵌套依赖版本失控问题,以 undici 为例
前端
用户52709648744907 小时前
Git 最佳实践
前端
星秀日7 小时前
JavaWeb--Ajax
前端·javascript·ajax
4_0_47 小时前
全栈视角:从零构建一个现代化的 Todo 应用
前端·node.js
BumBle8 小时前
uniapp 用css实现圆形进度条组件
前端·vue.js·uni-app
杏花春雨江南8 小时前
npm error Could not resolve dependency:
前端·npm·node.js