mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-06 17:18:40 +08:00
* chore: 🚀 引入vitest做组件测试 * chore: 🚀 引入vitest做组件测试 * chore: 🚀 update workflow * chore: 🚀 update workflow * chore: 🚀 update workflow * chore: 🚀 update workflow * chore: 🚀 update nodejs version * chore: 🚀 update nodejs version
7.9 KiB
7.9 KiB
wot-design-uni 测试指南
本文档提供了 wot-design-uni 组件库的测试策略、工作流程和最佳实践指南。
目录
测试策略
wot-design-uni 采用以下测试策略:
分层测试
- 单元测试:测试组件的独立功能和属性
- 集成测试:测试组件之间的交互和组合使用
- 快照测试:确保 UI 不会意外变化
测试覆盖率目标
- 整体代码覆盖率:85%+
- 关键组件(如 ConfigProvider、Button、Input 等):90%+
- 工具函数:95%+
测试粒度
- 每个组件至少有一个测试文件
- 每个组件的主要功能都应有对应的测试用例
- 边缘情况和错误处理也应有测试用例
测试平台
- H5 平台:所有组件都在 H5 平台上测试
- 条件编译:虽然我们只在 H5 平台上测试,但测试环境会正确处理条件编译代码
测试工作流
wot-design-uni 使用 GitHub Actions 自动化测试流程,主要工作流文件是 .github/workflows/component-testing.yml。
触发条件
测试工作流在以下情况下会自动触发:
- 推送到主分支:当代码推送到 main、master 或 dev 分支,且修改了组件相关文件时
- 创建 Pull Request:当创建针对 main、master 或 dev 分支的 PR,且修改了组件相关文件时
- 定时运行:每周一凌晨 3 点自动运行所有测试
- 手动触发:可以在 GitHub Actions 页面手动触发,并指定要测试的组件
工作流步骤
-
确定测试矩阵:
- 根据变更的文件确定需要测试的组件
-
ESLint 检查:
- 运行 ESLint 检查,确保代码质量
-
组件测试:
- 针对确定的组件运行 H5 平台测试
- 生成测试覆盖率报告
- 上传测试结果到 Codecov
-
测试摘要:
- 生成测试摘要报告
- 在 PR 中添加测试结果评论
如何运行测试
本地运行测试
运行所有测试
# 运行所有测试
pnpm test
# 运行所有测试并生成覆盖率报告
pnpm coverage
# 在 H5 平台上运行所有测试
pnpm test:h5
GitHub Actions 中运行测试
- 导航到仓库的 Actions 标签页
- 从左侧列表中选择 "Component Testing" 工作流
- 点击 "Run workflow" 按钮
- 可以选择性地指定要测试的组件名称(例如:wd-button)
- 点击 "Run workflow" 开始测试
查看测试结果
测试完成后,你可以:
- 在工作流运行详情页面查看测试结果
- 下载测试报告和覆盖率报告
- 在 Codecov 上查看详细的覆盖率信息
- 如果是 PR,可以在 PR 评论中看到测试摘要
编写测试
测试文件结构
测试文件应放在 tests/components 目录下,命名为 {组件名}.test.ts。
每个测试文件的基本结构如下:
import { mount } from '@vue/test-utils'
import { describe, test, expect, vi } from 'vitest'
import WdComponent from '../../src/uni_modules/wot-design-uni/components/wd-component/wd-component.vue'
describe('WdComponent', () => {
// 测试基本渲染
test('基本渲染', () => {
const wrapper = mount(WdComponent)
expect(wrapper.classes()).toContain('wd-component')
})
// 更多测试...
})
测试用例编写指南
1. 测试组件渲染
test('基本渲染', () => {
const wrapper = mount(WdButton)
expect(wrapper.classes()).toContain('wd-button')
})
2. 测试组件属性
test('按钮类型', () => {
const wrapper = mount(WdButton, {
props: { type: 'primary' }
})
expect(wrapper.classes()).toContain('wd-button--primary')
})
3. 测试组件事件
test('点击事件', async () => {
const wrapper = mount(WdButton)
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toBeTruthy()
})
4. 测试组件插槽
test('默认插槽', () => {
const wrapper = mount(WdButton, {
slots: { default: '按钮文本' }
})
expect(wrapper.text()).toContain('按钮文本')
})
5. 测试异步行为
test('异步加载', async () => {
const wrapper = mount(WdComponent, {
props: { loading: true }
})
expect(wrapper.classes()).toContain('is-loading')
await wrapper.setProps({ loading: false })
expect(wrapper.classes()).not.toContain('is-loading')
})
条件编译测试
虽然我们只在 H5 平台上测试,但可以使用条件编译来测试平台特定代码:
test('平台特定功能', () => {
// 我们在 H5 平台上测试
// process.env.UNI_PLATFORM 会被设置为 'h5'
if (process.env.UNI_PLATFORM === 'h5') {
// H5 特定测试
// ...
}
})
最佳实践
1. 使用真实组件
- 测试真实组件,而不是模拟组件
- 只在必要时模拟依赖
// 推荐
const wrapper = mount(WdButton)
// 不推荐(除非必要)
const MockButton = {
template: '<button class="wd-button"></button>'
}
const wrapper = mount(MockButton)
2. 测试边缘情况
- 测试组件在各种边缘情况下的行为
- 包括错误处理、极限值等
test('处理无效输入', () => {
const wrapper = mount(WdInput, {
props: { maxlength: 5 }
})
wrapper.setValue('123456')
expect(wrapper.vm.value).toBe('12345')
})
3. 保持测试简单
- 每个测试只测试一个功能点
- 避免复杂的测试逻辑
// 推荐
test('按钮禁用状态', () => {
const wrapper = mount(WdButton, {
props: { disabled: true }
})
expect(wrapper.classes()).toContain('is-disabled')
})
test('按钮禁用时不触发点击事件', async () => {
const wrapper = mount(WdButton, {
props: { disabled: true }
})
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toBeFalsy()
})
// 不推荐
test('按钮禁用状态和事件', async () => {
const wrapper = mount(WdButton, {
props: { disabled: true }
})
expect(wrapper.classes()).toContain('is-disabled')
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toBeFalsy()
})
4. 使用中文测试描述
- 使用中文编写测试用例的标题,保持一致性
// 推荐
test('基本渲染', () => {
// ...
})
// 不推荐
test('renders with default props', () => {
// ...
})
5. 定期更新测试
- 随着组件的更新,及时更新测试用例
- 保持测试覆盖率不下降
常见问题
1. 测试中的异步问题
如果测试中的异步行为不按预期工作,可以尝试:
// 使用 await nextTick()
import { nextTick } from 'vue'
test('异步行为', async () => {
const wrapper = mount(WdComponent)
wrapper.vm.doSomethingAsync()
await nextTick()
// 断言...
})
// 或使用 setTimeout
test('延迟行为', async () => {
const wrapper = mount(WdComponent)
wrapper.vm.doSomethingWithDelay()
await new Promise(resolve => setTimeout(resolve, 100))
// 断言...
})
2. 模拟 uni-app API
在测试中模拟 uni-app API:
// 在 setup.ts 中
vi.mock('uni-app', () => ({
uni: {
showToast: vi.fn(),
navigateTo: vi.fn(),
// 其他需要模拟的 API...
}
}))
// 在测试中
import { uni } from 'uni-app'
test('调用 uni API', async () => {
const wrapper = mount(WdComponent)
await wrapper.find('.trigger').trigger('click')
expect(uni.showToast).toHaveBeenCalled()
})