关于 frontend-testing
供 Claude 按既定项目规范编写和审查前端测试的技能,涵盖逐文件迭代测试、偏重集成测试的实践,以及 Vitest 和 React Testing Library 使用指引。
主要功能
- 01生成与审查前端测试
- 02逐文件增量工作流
- 03集成测试指引
- 04模拟与异步测试模式
- 05覆盖率和场景清单
适用场景
- 01为 React 组件或 Hook 编写测试
- 02检查前端测试的完整性
- 03提升前端目录的测试覆盖率
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pageai-pro/ ralph-loop --skill frontend-testing安装后在 Claude Code 中输入/frontend-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/component-test.template.tsxhook-test.template.tsutility-test.template.tsreferences/async-testing.mdchecklist.mdcommon-patterns.mdmocking.mdworkflow.md详细介绍
这项技能可帮助 Claude 遵循既定规范和最佳实践,生成高质量、全面的前端测试。
何时应用此技能
在用户出现以下情况时应用此技能:
- 要求为组件、Hook 或工具函数编写测试
- 要求检查现有测试是否完整
- 提到 Vitest、React Testing Library、RTL 或 spec 文件
- 要求提高测试覆盖率
- 提到前端代码的测试、单元测试或集成测试
- 想了解前端代码库中的测试模式
以下情况不要应用:
- 用户询问的是 E2E 测试(Playwright)
- 用户只提出概念性问题,没有代码上下文
快速参考
技术栈
| 工具 | 版本 | 用途 |
|---|---|---|
| Vitest | 4+ | 测试运行器 |
| React Testing Library | 16+ | 组件测试 |
| jsdom | - | 测试环境 |
| TypeScript | 5+ | 类型安全 |
注意:请根据项目的依赖项及时更新此列表。
关键命令
为了更快获得反馈,始终优先运行指定测试,而不是整个测试套件:
# Run ALL tests (avoid during development)
npx vitest run
# ✅ PREFERRED: Run specific file
npx vitest run src/components/Button.spec.tsx
# ✅ PREFERRED: Run tests matching a pattern
npx vitest run --grep "Button"
npx vitest run -t "should render"
# ✅ PREFERRED: Run specific describe block
npx vitest run --grep "Button > Rendering"
# ✅ Run tests in a directory
npx vitest run src/components/
# ✅ Run single test by name
npx vitest run -t "should disable button when loading"
监视模式(后台测试)
使用监视模式,高效地迭代开发:
# ✅ Watch mode - reruns on file changes
npx vitest
# ✅ Watch specific file
npx vitest src/components/Button.spec.tsx
# ✅ Watch tests matching pattern
npx vitest --grep "Button"
文件命名
- 测试文件:
ComponentName.spec.tsx(与组件放在同一目录)
测试结构模板
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import Component from './index'
// ✅ Import real project components (DO NOT mock these)
// import Loading from '@/app/components/base/loading'
// import { ChildComponent } from './child-component'
// ✅ Mock external dependencies only
vi.mock('@/service/api')
vi.mock('next/navigation', () => ({
useRouter: () => ({ push: vi.fn() }),
usePathname: () => '/test',
}))
// Shared state for mocks (if needed)
let mockSharedState = false
describe('ComponentName', () => {
beforeEach(() => {
vi.clearAllMocks() // ✅ Reset mocks BEFORE each test
mockSharedState = false // ✅ Reset shared state
})
// Rendering tests (REQUIRED)
describe('Rendering', () => {
it('should render without crashing', () => {
// Arrange
const props = { title: 'Test' }
// Act
render(<Component {...props} />)
// Assert
expect(screen.getByText('Test')).toBeInTheDocument()
})
})
// Props tests (REQUIRED)
describe('Props', () => {
it('should apply custom className', () => {
render(<Component className="custom" />)
expect(screen.getByRole('button')).toHaveClass('custom')
})
})
// User Interactions
describe('User Interactions', () => {
it('should handle click events', () => {
const handleClick = vi.fn()
render(<Component onClick={handleClick} />)
fireEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
})
// Edge Cases (REQUIRED)
describe('Edge Cases', () => {
it('should handle null data', () => {
render(<Component data={null} />)
expect(screen.getByText(/no data/i)).toBeInTheDocument()
})
it('should handle empty array', () => {
render(<Component items={[]} />)
expect(screen.getByText(/empty/i)).toBeInTheDocument()
})
})
})
测试工作流(重要)
⚠️ 必须采用增量方式
绝不要一次性生成所有测试文件。 对于复杂组件或包含多个文件的目录:
- 分析并规划:列出所有文件,按复杂度从低到高排序
- 逐个处理:编写测试 → 运行测试 → 必要时修复 → 继续下一个
- 继续前先验证:当前文件的测试通过之前,不要处理下一个文件
For each file:
┌────────────────────────────────────────────────┐
│ 1. Write test │
│ 2. Run: npm run test-unit <file>.spec.tsx │
│ 3. PASS? → Mark complete, next file │
│ FAIL? → Fix first, then continue │
└────────────────────────────────────────────────┘
按复杂度排序
对多个文件进行测试时,按以下顺序处理:
- 🟢 工具函数(最简单)
- 🟢 自定义 Hook
- 🟡 简单组件(展示型)
- 🟡 中等复杂度组件(包含状态、效果)
- 🔴 复杂组件(涉及 API、路由)
- 🔴 集成测试(index 文件,最后处理)
何时先重构
- 中等复杂度:测试前拆分为更小的部分
- 500+ 行:考虑在测试前拆分
- 依赖项较多:先将逻辑提取到 Hook 中
测试策略
按路径测试(目录测试)
分配到某个目录/路径的测试任务时,测试该路径下的所有内容:
- 测试目录中的所有组件、Hook 和工具函数(不只是
index文件) - 采用增量方式:一次处理一个文件,每个文件都要先验证再继续
- 目标:覆盖目录中的所有文件,覆盖率达到 100%
优先进行集成测试
为目录编写测试时,优先采用集成测试:
- ✅ 直接导入项目中的真实组件(包括基础组件和同级组件)
- ✅ 仅模拟:API 服务(
@/service/*)、next/navigation、复杂的上下文提供器 - ❌ 不要模拟基础组件(
@/app/components/base/*) - ❌ 不要模拟同一目录中的同级/子组件
核心原则
1. AAA 模式(准备-执行-断言)
每项测试都应清晰地区分:
- 准备(Arrange):设置测试数据并渲染组件
- 执行(Act):执行用户操作
- 断言(Assert):验证预期结果
2. 黑盒测试
- 测试可观察到的行为,而非实现细节
- 使用语义化查询(getByRole、getByLabelText)
- 避免直接测试内部状态
- 断言中优先使用模式匹配,而非硬编码字符串:
// ❌ Avoid: hardcoded text assertions
expect(screen.getByText('Loading...')).toBeInTheDocument()
// ✅ Better: role-based queries
expect(screen.getByRole('status')).toBeInTheDocument()
// ✅ Better: pattern matching
expect(screen.getByText(/loading/i)).toBeInTheDocument()
3. 每项测试只验证一种行为
每项测试只验证一种用户可观察到的行为:
// ✅ Good: One behavior
it('should disable button when loading', () => {
render(<Button loading />)
expect(screen.getByRole('button')).toBeDisabled()
})
// ❌ Bad: Multiple behaviors
it('should handle loading state', () => {
render(<Button loading />)
expect(screen.getByRole('button')).toBeDisabled()
expect(screen.getByText('Loading...')).toBeInTheDocument()
expect(screen.getByRole('button')).toHaveClass('loading')
})
4. 语义化命名
使用 should <behavior> when <condition>:
it('should show error message when validation fails')
it('should call onSubmit when form is valid')
it('should disable input when isReadOnly is true')
必需的测试场景
始终必需(所有组件)
- 渲染:组件渲染时不会崩溃
- Props:必需的 props、可选的 props、默认值
- 边界情况:null、undefined、空值、边界条件
条件性要求(存在时)
| 功能 | 测试重点 |
|---|---|
useState | 初始状态、状态转换、清理 |
useEffect | 执行、依赖项、清理 |
| 事件处理程序 | 所有 onClick、onChange、onSubmit、键盘操作 |
| API 调用 | 加载、成功、错误状态 |
| 路由 | 导航、参数、查询字符串 |
useCallback/useMemo | 引用相等性 |
| Context | Provider 值、消费者行为 |
| 表单 | 验证、提交、错误显示 |
覆盖率目标(每个文件)
对于生成的每个测试文件,目标是:
- ✅ 100% 函数覆盖率
- ✅ 100% 语句覆盖率
- ✅ >95% 分支覆盖率
- ✅ >95% 行覆盖率
注意:对于包含多个文件的目录,每次处理一个文件,并确保每个文件都达到完整覆盖率。请参阅
references/workflow.md。
详细指南
如需了解更多详细信息,请参阅:
references/workflow.md- 增量测试工作流(测试多个文件时必须阅读)references/mocking.md- Mock 模式和最佳实践references/async-testing.md- 异步操作和 API 调用references/common-patterns.md- 常用测试模式references/checklist.md- 测试生成检查清单和验证步骤
项目配置
vitest.config.ts- Vitest 配置vitest.setup.ts- 测试环境设置- 不会自动 Mock 模块。全局 Mock 位于
vitest.setup.ts中(例如react-i18next、next/image);请在测试文件中局部 Mock 其他模块,例如ky或mime。
评论
支持 Markdown