# test-utils **Repository Path**: vitarx/test-utils ## Basic Information - **Project Name**: test-utils - **Description**: Vitarx 官方测试工具库,提供类似 `@vue/test-utils` 的 API 风格,专为 `vitest` 测试框架优化设计。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-05 - **Last Updated**: 2026-03-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # @vitarx/test-utils [![npm version](https://badge.fury.io/js/@vitarx%2Ftest-utils.svg)](https://badge.fury.io/js/@vitarx/test-utils) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) > Vitarx 官方测试工具库,提供简洁优雅的组件测试体验 ## 📖 简介 `@vitarx/test-utils` 是专为 Vitarx 框架设计的测试工具库,提供了完整的组件挂载、DOM 操作和异步处理功能。 ## ✨ 特性 - 🎯 **简单直观** - API 设计简洁明了,易于理解和使用 - 🔧 **功能完整** - 支持组件挂载、DOM 查找、事件触发等完整测试流程 - 📦 **TypeScript 支持** - 完整的类型定义,提供优秀的 IDE 智能提示 - ⚡️ **异步处理** - 内置 Promise 微任务队列清空工具 - 🎭 **DOM 桩支持** - 支持 DOM 级别的元素替换,简化复杂组件测试 - 🧪 **测试框架无关** - 可与 Vitest、Jest 等任何测试框架配合使用 ## 📦 安装 ```bash # 使用 npm npm install -D @vitarx/test-utils # 使用 yarn yarn add -D @vitarx/test-utils # 使用 pnpm pnpm add -D @vitarx/test-utils ``` ## 🚀 快速开始 ### 基本示例 ```tsx import { describe, it, expect } from 'vitest' import { mount } from '@vitarx/test-utils' import { ref } from 'vitarx' function Counter() { const count = ref(0) const increment = () => count.value++ return (
{count}
) } describe('计数器组件', () => { it('应该正确渲染初始值', () => { const wrapper = mount(Counter) expect(wrapper.find('.count')?.text()).toBe('0') wrapper.unmount() }) it('点击按钮应该增加计数', async () => { const wrapper = mount(Counter) const button = wrapper.find('.increment') await button?.trigger('click') expect(wrapper.find('.count')?.text()).toBe('1') wrapper.unmount() }) }) ``` ## 📚 API 文档 ### 核心函数 #### `mount(component, options?)` 将组件挂载到测试环境,返回 Wrapper 实例。 **参数:** - `component` - 要挂载的组件函数 - `options` - 可选的挂载配置对象 - `props` - 传递给组件的属性 - `attachTo` - 自定义挂载容器元素 - `domStubs` - DOM 级别桩替换配置 **返回值:** `Wrapper` 实例 **示例:** ```tsx // 基本用法 const wrapper = mount(MyComponent) const count = ref(42) // 传递 props const wrapper = mount(MyComponent, { props: { title: 'Hello World', get count() { // 动态参数 return count.value } } }) // 使用自定义容器 const container = document.createElement('div') document.body.appendChild(container) const wrapper = mount(MyComponent, { attachTo: container }) // 使用 DOM 桩替换 const wrapper = mount(ParentComponent, { domStubs: { '.child-component': '
Mocked
' } }) ``` #### `flushPromises()` 清空当前事件循环中的 Promise 微任务队列。 **返回值:** `Promise` **使用场景:** - 组件内部存在异步逻辑(如 Promise.then / async 函数) - 需要确保异步操作完成后再进行断言 **示例:** ```tsx import { mount, flushPromises } from '@vitarx/test-utils' async function AsyncComponent() { const data = ref('') const loadData = async () => { const result = await fetchData() data.value = result } return (
{data}
) } it('应该正确加载异步数据', async () => { const wrapper = mount(AsyncComponent) await wrapper.find('.load')?.trigger('click') await flushPromises() // 等待所有 Promise 完成 expect(wrapper.find('.data')?.text()).toBe('loaded') wrapper.unmount() }) ``` ### Wrapper 类 Wrapper 是测试工具的核心类,提供了丰富的 DOM 操作和查询方法。 #### 属性访问 ##### `wrapper.props` 获取组件的 props 对象(只读)。 ```tsx function MyComponent(props: { message: string }) { return
{props.message}
} const wrapper = mount(MyComponent, { props: { message: 'Hello' } }) console.log(wrapper.props.message) // 'Hello' ``` ##### `wrapper.view` 获取内部的视图对象实例。 ```tsx const wrapper = mount(MyComponent) const view = wrapper.view // 访问底层 DOM 节点 console.log(view.node.tagName) ``` #### DOM 查询 ##### `find(selector)` 查找匹配选择器的第一个元素。 ```tsx const wrapper = mount(MyComponent) // 查找单个元素 const button = wrapper.find('button') const item = wrapper.find('.item') const deep = wrapper.find('.container .child') if (button) { await button.trigger('click') } ``` ##### `findAll(selector)` 查找匹配选择器的所有元素。 ```tsx const wrapper = mount(() => (
Item 1 Item 2 Item 3
)) const items = wrapper.findAll('.item') console.log(items.length) // 3 items.forEach(item => console.log(item.text())) ``` #### 属性操作 ##### `attributes()` 获取元素的所有属性。 ```tsx const wrapper = mount(() => (
Content
)) const attrs = wrapper.attributes() console.log(attrs) // { id: 'test', class: 'container', 'data-value': '123' } ``` ##### `attributes(key)` 获取元素的指定属性值。 ```tsx const wrapper = mount(() => (
Content
)) const id = wrapper.attributes('id') console.log(id) // 'test' const notExist = wrapper.attributes('not-exist') console.log(notExist) // '' ``` #### 类名操作 ##### `classes()` 获取元素的所有类名。 ```tsx const wrapper = mount(() => (
Content
)) const classes = wrapper.classes() console.log(classes) // ['active', 'visible', 'highlighted'] ``` ##### `classes(className)` 检查元素是否包含指定类名。 ```tsx const wrapper = mount(() => (
Content
)) console.log(wrapper.classes('active')) // true console.log(wrapper.classes('hidden')) // false ``` #### 状态检查 ##### `exists()` 检查元素是否存在于 DOM 树中。 ```tsx const wrapper = mount(MyComponent) console.log(wrapper.exists()) // true wrapper.unmount() console.log(wrapper.exists()) // false ``` ##### `isVisible()` 检查元素是否可见。 元素需要满足以下条件才被视为可见: 1. 存在于文档中 2. `display` 不为 `none` 3. `visibility` 不为 `hidden` ```tsx const wrapper1 = mount(() =>
Visible
) console.log(wrapper1.isVisible()) // true const wrapper2 = mount(() =>
Hidden
) console.log(wrapper2.isVisible()) // false const wrapper3 = mount(() =>
Hidden
) console.log(wrapper3.isVisible()) // false ``` #### 交互操作 ##### `trigger(event, payload?)` 在元素上触发事件。 ```tsx const wrapper = mount(() => ( )) // 触发点击事件 await wrapper.trigger('click') // 触发自定义事件并携带数据 await wrapper.trigger('custom', { message: 'test' }) ``` ##### `setValue(value)` 为表单元素设置值。 支持的元素类型: - `input` - 设置 value 并触发 input 和 change 事件 - `textarea` - 设置 value 并触发 input 和 change 事件 - `select` - 根据 value 匹配 option 并触发 change 事件 ```tsx // 设置 input 值 const input = wrapper.find('input') await input?.setValue('new value') // 设置 select 值 const select = wrapper.find('select') await select?.setValue('option2') // 设置 textarea 值 const textarea = wrapper.find('textarea') await textarea?.setValue('textarea content') ``` #### 内容获取 ##### `html()` 获取元素的 HTML 字符串表示。 ```tsx const wrapper = mount(() => (
Hello
)) console.log(wrapper.html()) // '
Hello
' ``` ##### `text()` 获取元素的文本内容。 ```tsx const wrapper = mount(() => (
Hello World
)) console.log(wrapper.text()) // 'Hello World' ``` #### 生命周期 ##### `unmount()` 卸载组件并清理资源。 ```tsx const wrapper = mount(MyComponent) // 使用 wrapper 进行测试... // 测试完成后卸载 wrapper.unmount() console.log(wrapper.exists()) // false ``` ## 🎯 使用场景 ### 测试异步组件 ```tsx import { mount, flushPromises } from '@vitarx/test-utils' import { ref } from 'vitarx' function AsyncDataComponent() { const data = ref(null) const loading = ref(false) const fetchData = async () => { loading.value = true const result = await fetch('/api/data') data.value = await result.json() loading.value = false } return (
{loading.value ? ( Loading... ) : ( {data.value} )}
) } describe('异步数据组件', () => { it('应该正确处理异步数据加载', async () => { const wrapper = mount(AsyncDataComponent) // 初始状态 expect(wrapper.find('.data')?.text()).toBe('') // 触发数据加载 await wrapper.find('.fetch')?.trigger('click') expect(wrapper.find('.loading')).toBeDefined() // 等待异步操作完成 await flushPromises() expect(wrapper.find('.data')?.text()).not.toBe('') wrapper.unmount() }) }) ``` ### 测试表单组件 ```tsx import { mount } from '@vitarx/test-utils' import { ref } from 'vitarx' function FormComponent() { const formData = ref({ username: '', email: '' }) const handleSubmit = () => { console.log('Submit:', formData.value) } return (
formData.value.username = e.target.value} /> formData.value.email = e.target.value} />
) } describe('表单组件', () => { it('应该正确处理表单输入', async () => { const wrapper = mount(FormComponent) // 填写表单 await wrapper.find('.username')?.setValue('john_doe') await wrapper.find('.email')?.setValue('john@example.com') // 提交表单 await wrapper.find('.submit')?.trigger('click') wrapper.unmount() }) }) ``` ### 使用 DOM 桩简化测试 ```tsx import { mount } from '@vitarx/test-utils' function ComplexParent() { return (
) } describe('复杂父组件', () => { it('应该正确渲染(使用桩替换)', () => { const wrapper = mount(ComplexParent, { domStubs: { '.child': '
Mocked Child
', '.widget': '
Mocked Widget
' } }) expect(wrapper.find('.child-stub')).toBeDefined() expect(wrapper.find('.widget-stub')).toBeDefined() wrapper.unmount() }) }) ``` ### 测试条件渲染 ```tsx import { mount } from '@vitarx/test-utils' import { ref } from 'vitarx' function ConditionalComponent() { const showDetails = ref(false) return (
{showDetails.value && (
Details Content
)}
) } describe('条件渲染组件', () => { it('应该正确切换显示状态', async () => { const wrapper = mount(ConditionalComponent) // 初始状态:详情不可见 expect(wrapper.find('.details')).toBeNull() // 点击切换按钮 await wrapper.find('.toggle')?.trigger('click') // 详情应该可见 expect(wrapper.find('.details')).toBeDefined() expect(wrapper.find('.details')?.text()).toBe('Details Content') // 再次点击切换按钮 await wrapper.find('.toggle')?.trigger('click') // 详情应该再次隐藏 expect(wrapper.find('.details')).toBeNull() wrapper.unmount() }) }) ``` ## 🔧 配置 ### Vitest 配置 ```typescript // vitest.config.ts import { defineConfig } from 'vitest/config' import vitarx from '@vitarx/plugin-vite' export default defineConfig({ plugins: [vitarx()], test: { environment: 'jsdom', globals: true } }) ``` ### TypeScript 配置 确保 `tsconfig.json` 包含以下配置: ```json { "compilerOptions": { "types": [ "vitarx", "vitest/globals" ] } } ``` ## ⚠️ 注意事项 1. **环境要求** - 需要在浏览器环境或 jsdom 环境运行(依赖 DOM API) 2. **资源清理** - 测试完成后建议调用 `wrapper.unmount()` 清理资源 3. **异步处理** - 涉及异步操作时,使用 `flushPromises()` 确保操作完成 4. **类型安全** - 充分利用 TypeScript 类型检查,避免运行时错误 ## 🤝 贡献 欢迎提交 Issue 和 Pull Request! ## 📄 License [MIT](LICENSE) ## 🔗 相关链接 - [Vitarx 官方文档](https://github.com/vitarx-lib/vitarx) - [GitHub 仓库](https://github.com/vitarx-lib/test-utils) - [NPM 包](https://www.npmjs.com/package/@vitarx/test-utils)