# 测试策略 > **文档版本**:v1.1 > **最后更新**:2025-01-18 > **状态**:⚠️ 规划中 --- ## 目录 1. [测试金字塔](#1-测试金字塔) 2. [单元测试](#2-单元测试) 3. [组件测试](#3-组件测试) 4. [Hook 测试](#4-hook-测试) 5. [E2E 测试](#5-e2e-测试) --- ## 1. 测试金字塔 ``` ┌──────────┐ │ E2E │ 少量关键流程 │ Tests │ ┌┴──────────┴┐ │Integration │ 主要业务流程 │ Tests │ ┌┴────────────┴┐ │ Unit Tests │ 工具函数、Hooks、组件 └──────────────┘ ``` --- ## 2. 单元测试 > **注意**:测试工具配置尚未在项目中实现,以下内容作为未来规划参考。 ### 2.1 Vitest 配置(规划) ```typescript // vitest.config.ts import { defineConfig } from "vitest/config"; import react from "@vitejs/plugin-react"; import path from "path"; export default defineConfig({ plugins: [react()], test: { globals: true, environment: "jsdom", setupFiles: ["./src/test/setup.ts"], include: ["src/**/*.{test,spec}.{ts,tsx}"], coverage: { reporter: ["text", "json", "html"], exclude: ["node_modules/", "src/test/"], }, }, resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, }); ``` ### 2.2 工具函数测试示例 ```typescript // src/utils/format.test.ts import { describe, it, expect } from "vitest"; import { formatDuration, formatFileSize } from "./format"; describe("formatDuration", () => { it("formats seconds to mm:ss", () => { expect(formatDuration(65)).toBe("01:05"); expect(formatDuration(3661)).toBe("01:01:01"); }); it("handles zero", () => { expect(formatDuration(0)).toBe("00:00"); }); }); describe("formatFileSize", () => { it("formats bytes correctly", () => { expect(formatFileSize(1024)).toBe("1 KB"); expect(formatFileSize(1048576)).toBe("1 MB"); }); }); ``` --- ## 3. 组件测试 ### 3.1 测试示例 ```typescript // src/components/features/storyboard/StoryboardItem.test.tsx import { describe, it, expect, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import { StoryboardItem } from './StoryboardItem'; const mockStoryboard = { id: '1', title: 'Test Storyboard', description: 'Test description', thumbnailUrl: '/test.jpg', duration: 5, order: 0, }; describe('StoryboardItem', () => { it('renders storyboard info', () => { render( {}} /> ); expect(screen.getByText('Test Storyboard')).toBeInTheDocument(); expect(screen.getByText('Test description')).toBeInTheDocument(); }); it('calls onSelect when clicked', () => { const onSelect = vi.fn(); render( ); fireEvent.click(screen.getByRole('button')); expect(onSelect).toHaveBeenCalledWith('1'); }); it('applies selected styles', () => { render( {}} /> ); expect(screen.getByRole('button')).toHaveClass('border-primary'); }); }); ``` --- ## 4. Hook 测试 ```typescript // src/hooks/useDebounce.test.ts import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { renderHook, act } from "@testing-library/react"; import { useDebounce } from "./useDebounce"; describe("useDebounce", () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it("returns initial value immediately", () => { const { result } = renderHook(() => useDebounce("hello", 500)); expect(result.current).toBe("hello"); }); it("debounces value changes", () => { const { result, rerender } = renderHook( ({ value }) => useDebounce(value, 500), { initialProps: { value: "hello" } }, ); rerender({ value: "world" }); expect(result.current).toBe("hello"); act(() => { vi.advanceTimersByTime(500); }); expect(result.current).toBe("world"); }); }); ``` --- ## 5. E2E 测试 ### 5.1 Playwright 配置(规划) ```typescript // playwright.config.ts import { defineConfig, devices } from "@playwright/test"; export default defineConfig({ testDir: "./e2e", fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: "html", use: { baseURL: "http://localhost:5173", trace: "on-first-retry", }, projects: [ { name: "chromium", use: { ...devices["Desktop Chrome"] }, }, ], webServer: { command: "npm run dev", url: "http://localhost:5173", reuseExistingServer: !process.env.CI, }, }); ``` ### 5.2 E2E 测试示例 ```typescript // e2e/editor.spec.ts import { test, expect } from "@playwright/test"; test.describe("Editor Page", () => { test.beforeEach(async ({ page }) => { await page.goto("/editor/project-1"); }); test("displays storyboard list", async ({ page }) => { await expect(page.getByTestId("storyboard-panel")).toBeVisible(); await expect(page.getByTestId("storyboard-item")).toHaveCount(5); }); test("can select a storyboard", async ({ page }) => { const firstItem = page.getByTestId("storyboard-item").first(); await firstItem.click(); await expect(firstItem).toHaveClass(/selected/); }); test("plays video on play button click", async ({ page }) => { await page.getByRole("button", { name: "播放" }).click(); await expect(page.getByRole("button", { name: "暂停" })).toBeVisible(); }); }); ``` --- ## 相关文档 - [技术栈概览](./01-tech-stack.md) - [开发规范](./13-dev-standards.md) - [性能优化](./10-performance.md) --- **最后更新**:2025-01-18 | **版本**:v1.1