🍀封装个 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')
  })
相关推荐
理人综艺好会8 分钟前
Web学习之用户认证
前端·学习
We་ct25 分钟前
LeetCode 36. 有效的数独:Set实现哈希表最优解
前端·算法·leetcode·typescript·散列表
weixin_3954489136 分钟前
main.c_cursor_0129
前端·网络·算法
2401_859049081 小时前
git submodule update --init --recursive无法拉取解决
前端·chrome·git
这是个栗子2 小时前
【Vue代码分析】前端动态路由传参与可选参数标记:实现“添加/查看”模式的灵活路由配置
前端·javascript·vue.js
刘一说2 小时前
Vue 动态路由参数丢失问题详解:为什么 `:id` 拿不到值?
前端·javascript·vue.js
熊猫钓鱼>_>2 小时前
动态网站发布部署核心问题详解
前端·nginx·容器化·网页开发·云服务器·静态部署
方也_arkling2 小时前
elementPlus按需导入配置
前端·javascript·vue.js
我的xiaodoujiao2 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 44--将自动化测试结果自动推送至钉钉工作群聊
前端·python·测试工具·ui·pytest
沛沛老爹2 小时前
Web开发者转型AI:多模态Agent视频分析技能开发实战
前端·人工智能·音视频