# 测试覆盖率提升实施计划 > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 将测试覆盖率从31.85%提升到70%,确保企业官网零风险上线 **Architecture:** 采用TDD(测试驱动开发)方法,优先补充核心业务模块测试,建立长期质量保障机制 **Tech Stack:** Jest + React Testing Library + TypeScript --- ## 📊 当前状态分析 **当前覆盖率:** 31.85% **已覆盖模块:** - ✅ API路由(auth, contact, health):94-100% - ✅ 核心业务组件(services, products, cases, news, testimonials, insights):100% - ✅ 工具库(utils, analytics, sanitize, constants):100% - ✅ 权限系统(permissions):100% **未覆盖模块:** - ❌ 页面组件(app/(marketing)/*):0% - ❌ 管理后台(app/admin/*):0% - ❌ 管理后台API(app/api/admin/*):0% - ❌ 特效组件(components/effects/*):0% --- ## 🎯 目标 **Phase 1(1-2周):** 提升覆盖率到50% **Phase 2(1-2周):** 提升覆盖率到70% **Phase 3(1周):** 建立长期质量保障机制 --- ## Phase 1: 页面组件测试(提升到50%) ### Task 1: 首页页面组件测试 **Files:** - Create: `src/app/(marketing)/page.test.tsx` - Test: `src/app/(marketing)/page.tsx` **Step 1: 查看首页页面源码** Run: ```bash cat src/app/\(marketing\)/page.tsx | head -100 ``` Expected: 显示首页页面结构和功能 **Step 2: 编写首页页面测试** Create: `src/app/(marketing)/page.test.tsx` ```typescript import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import HomePage from './page'; describe('HomePage', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render home page', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should render hero section', () => { render(); const heroSection = document.querySelector('#hero'); expect(heroSection).toBeInTheDocument(); }); it('should render services section', () => { render(); const servicesSection = document.querySelector('#services'); expect(servicesSection).toBeInTheDocument(); }); it('should render products section', () => { render(); const productsSection = document.querySelector('#products'); expect(productsSection).toBeInTheDocument(); }); it('should render cases section', () => { render(); const casesSection = document.querySelector('#cases'); expect(casesSection).toBeInTheDocument(); }); it('should render news section', () => { render(); const newsSection = document.querySelector('#news'); expect(newsSection).toBeInTheDocument(); }); it('should render contact section', () => { render(); const contactSection = document.querySelector('#contact'); expect(contactSection).toBeInTheDocument(); }); }); describe('Accessibility', () => { it('should have main landmark', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should have proper heading hierarchy', () => { render(); const h1 = screen.getByRole('heading', { level: 1 }); expect(h1).toBeInTheDocument(); }); }); describe('SEO', () => { it('should have meta description', () => { render(); const metaDescription = document.querySelector('meta[name="description"]'); expect(metaDescription).toBeInTheDocument(); }); }); }); ``` **Step 3: 运行测试验证通过** Run: ```bash npm run test:unit -- src/app/\(marketing\)/page.test.tsx ``` Expected: 所有测试通过 **Step 4: 提交代码** ```bash git add src/app/\(marketing\)/page.test.tsx git commit -m "test: add home page component tests" ``` --- ### Task 2: 关于我们页面组件测试 **Files:** - Create: `src/app/(marketing)/about/page.test.tsx` - Test: `src/app/(marketing)/about/page.tsx` **Step 1: 查看关于我们页面源码** Run: ```bash cat src/app/\(marketing\)/about/page.tsx | head -100 ``` Expected: 显示关于我们页面结构和功能 **Step 2: 编写关于我们页面测试** Create: `src/app/(marketing)/about/page.test.tsx` ```typescript import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import AboutPage from './page'; describe('AboutPage', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render about page', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should render company introduction', () => { render(); const intro = screen.getByText(/关于我们/i); expect(intro).toBeInTheDocument(); }); it('should render company history', () => { render(); const history = screen.getByText(/发展历程/i); expect(history).toBeInTheDocument(); }); it('should render company culture', () => { render(); const culture = screen.getByText(/企业文化/i); expect(culture).toBeInTheDocument(); }); it('should render team members', () => { render(); const team = screen.getByText(/团队/i); expect(team).toBeInTheDocument(); }); }); describe('Accessibility', () => { it('should have main landmark', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should have proper heading hierarchy', () => { render(); const h1 = screen.getByRole('heading', { level: 1 }); expect(h1).toBeInTheDocument(); }); }); }); ``` **Step 3: 运行测试验证通过** Run: ```bash npm run test:unit -- src/app/\(marketing\)/about/page.test.tsx ``` Expected: 所有测试通过 **Step 4: 提交代码** ```bash git add src/app/\(marketing\)/about/page.test.tsx git commit -m "test: add about page component tests" ``` --- ### Task 3: 产品页面组件测试 **Files:** - Create: `src/app/(marketing)/products/page.test.tsx` - Test: `src/app/(marketing)/products/page.tsx` **Step 1: 查看产品页面源码** Run: ```bash cat src/app/\(marketing\)/products/page.tsx | head -100 ``` Expected: 显示产品页面结构和功能 **Step 2: 编写产品页面测试** Create: `src/app/(marketing)/products/page.test.tsx` ```typescript import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import ProductsPage from './page'; describe('ProductsPage', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render products page', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should render products list', () => { render(); const productsList = screen.getByRole('list'); expect(productsList).toBeInTheDocument(); }); it('should render product cards', () => { render(); const productCards = screen.getAllByRole('listitem'); expect(productCards.length).toBeGreaterThan(0); }); it('should render product categories', () => { render(); const categories = screen.getByText(/产品分类/i); expect(categories).toBeInTheDocument(); }); }); describe('Navigation', () => { it('should have product detail links', () => { render(); const links = screen.getAllByRole('link'); expect(links.length).toBeGreaterThan(0); }); }); describe('Accessibility', () => { it('should have main landmark', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should have proper heading hierarchy', () => { render(); const h1 = screen.getByRole('heading', { level: 1 }); expect(h1).toBeInTheDocument(); }); }); }); ``` **Step 3: 运行测试验证通过** Run: ```bash npm run test:unit -- src/app/\(marketing\)/products/page.test.tsx ``` Expected: 所有测试通过 **Step 4: 提交代码** ```bash git add src/app/\(marketing\)/products/page.test.tsx git commit -m "test: add products page component tests" ``` --- ### Task 4: 服务页面组件测试 **Files:** - Create: `src/app/(marketing)/services/page.test.tsx` - Test: `src/app/(marketing)/services/page.tsx` **Step 1: 查看服务页面源码** Run: ```bash cat src/app/\(marketing\)/services/page.tsx | head -100 ``` Expected: 显示服务页面结构和功能 **Step 2: 编写服务页面测试** Create: `src/app/(marketing)/services/page.test.tsx` ```typescript import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import ServicesPage from './page'; describe('ServicesPage', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render services page', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should render services list', () => { render(); const servicesList = screen.getByRole('list'); expect(servicesList).toBeInTheDocument(); }); it('should render service cards', () => { render(); const serviceCards = screen.getAllByRole('listitem'); expect(serviceCards.length).toBeGreaterThan(0); }); it('should render service categories', () => { render(); const categories = screen.getByText(/服务分类/i); expect(categories).toBeInTheDocument(); }); }); describe('Navigation', () => { it('should have service detail links', () => { render(); const links = screen.getAllByRole('link'); expect(links.length).toBeGreaterThan(0); }); }); describe('Accessibility', () => { it('should have main landmark', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should have proper heading hierarchy', () => { render(); const h1 = screen.getByRole('heading', { level: 1 }); expect(h1).toBeInTheDocument(); }); }); }); ``` **Step 3: 运行测试验证通过** Run: ```bash npm run test:unit -- src/app/\(marketing\)/services/page.test.tsx ``` Expected: 所有测试通过 **Step 4: 提交代码** ```bash git add src/app/\(marketing\)/services/page.test.tsx git commit -m "test: add services page component tests" ``` --- ### Task 5: 联系页面组件测试 **Files:** - Create: `src/app/(marketing)/contact/page.test.tsx` - Test: `src/app/(marketing)/contact/page.tsx` **Step 1: 查看联系页面源码** Run: ```bash cat src/app/\(marketing\)/contact/page.tsx | head -100 ``` Expected: 显示联系页面结构和功能 **Step 2: 编写联系页面测试** Create: `src/app/(marketing)/contact/page.test.tsx` ```typescript import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import ContactPage from './page'; describe('ContactPage', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render contact page', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should render contact form', () => { render(); const form = screen.getByRole('form'); expect(form).toBeInTheDocument(); }); it('should render name input', () => { render(); const nameInput = screen.getByLabelText(/姓名/i); expect(nameInput).toBeInTheDocument(); }); it('should render email input', () => { render(); const emailInput = screen.getByLabelText(/邮箱/i); expect(emailInput).toBeInTheDocument(); }); it('should render message textarea', () => { render(); const messageTextarea = screen.getByLabelText(/留言/i); expect(messageTextarea).toBeInTheDocument(); }); it('should render submit button', () => { render(); const submitButton = screen.getByRole('button', { name: /提交/i }); expect(submitButton).toBeInTheDocument(); }); }); describe('Form Validation', () => { it('should show error for empty name', async () => { render(); const submitButton = screen.getByRole('button', { name: /提交/i }); fireEvent.click(submitButton); await waitFor(() => { expect(screen.getByText(/请输入姓名/i)).toBeInTheDocument(); }); }); it('should show error for invalid email', async () => { render(); const emailInput = screen.getByLabelText(/邮箱/i); fireEvent.change(emailInput, { target: { value: 'invalid-email' } }); const submitButton = screen.getByRole('button', { name: /提交/i }); fireEvent.click(submitButton); await waitFor(() => { expect(screen.getByText(/请输入有效的邮箱/i)).toBeInTheDocument(); }); }); }); describe('Form Submission', () => { it('should submit form successfully', async () => { render(); const nameInput = screen.getByLabelText(/姓名/i); const emailInput = screen.getByLabelText(/邮箱/i); const messageTextarea = screen.getByLabelText(/留言/i); const submitButton = screen.getByRole('button', { name: /提交/i }); fireEvent.change(nameInput, { target: { value: '张三' } }); fireEvent.change(emailInput, { target: { value: 'zhangsan@example.com' } }); fireEvent.change(messageTextarea, { target: { value: '这是一条测试留言' } }); fireEvent.click(submitButton); await waitFor(() => { expect(screen.getByText(/提交成功/i)).toBeInTheDocument(); }); }); }); describe('Accessibility', () => { it('should have main landmark', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should have proper heading hierarchy', () => { render(); const h1 = screen.getByRole('heading', { level: 1 }); expect(h1).toBeInTheDocument(); }); it('should have accessible form labels', () => { render(); const nameInput = screen.getByLabelText(/姓名/i); const emailInput = screen.getByLabelText(/邮箱/i); const messageTextarea = screen.getByLabelText(/留言/i); expect(nameInput).toHaveAttribute('aria-label'); expect(emailInput).toHaveAttribute('aria-label'); expect(messageTextarea).toHaveAttribute('aria-label'); }); }); }); ``` **Step 3: 运行测试验证通过** Run: ```bash npm run test:unit -- src/app/\(marketing\)/contact/page.test.tsx ``` Expected: 所有测试通过 **Step 4: 提交代码** ```bash git add src/app/\(marketing\)/contact/page.test.tsx git commit -m "test: add contact page component tests" ``` --- ### Task 6: 新闻页面组件测试 **Files:** - Create: `src/app/(marketing)/news/page.test.tsx` - Test: `src/app/(marketing)/news/page.tsx` **Step 1: 查看新闻页面源码** Run: ```bash cat src/app/\(marketing\)/news/page.tsx | head -100 ``` Expected: 显示新闻页面结构和功能 **Step 2: 编写新闻页面测试** Create: `src/app/(marketing)/news/page.test.tsx` ```typescript import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import NewsPage from './page'; describe('NewsPage', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render news page', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should render news list', () => { render(); const newsList = screen.getByRole('list'); expect(newsList).toBeInTheDocument(); }); it('should render news cards', () => { render(); const newsCards = screen.getAllByRole('listitem'); expect(newsCards.length).toBeGreaterThan(0); }); it('should render news categories', () => { render(); const categories = screen.getByText(/新闻分类/i); expect(categories).toBeInTheDocument(); }); }); describe('Navigation', () => { it('should have news detail links', () => { render(); const links = screen.getAllByRole('link'); expect(links.length).toBeGreaterThan(0); }); }); describe('Accessibility', () => { it('should have main landmark', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should have proper heading hierarchy', () => { render(); const h1 = screen.getByRole('heading', { level: 1 }); expect(h1).toBeInTheDocument(); }); }); }); ``` **Step 3: 运行测试验证通过** Run: ```bash npm run test:unit -- src/app/\(marketing\)/news/page.test.tsx ``` Expected: 所有测试通过 **Step 4: 提交代码** ```bash git add src/app/\(marketing\)/news/page.test.tsx git commit -m "test: add news page component tests" ``` --- ### Task 7: 案例页面组件测试 **Files:** - Create: `src/app/(marketing)/cases/page.test.tsx` - Test: `src/app/(marketing)/cases/page.tsx` **Step 1: 查看案例页面源码** Run: ```bash cat src/app/\(marketing\)/cases/page.tsx | head -100 ``` Expected: 显示案例页面结构和功能 **Step 2: 编写案例页面测试** Create: `src/app/(marketing)/cases/page.test.tsx` ```typescript import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import CasesPage from './page'; describe('CasesPage', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render cases page', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should render cases list', () => { render(); const casesList = screen.getByRole('list'); expect(casesList).toBeInTheDocument(); }); it('should render case cards', () => { render(); const caseCards = screen.getAllByRole('listitem'); expect(caseCards.length).toBeGreaterThan(0); }); it('should render case categories', () => { render(); const categories = screen.getByText(/案例分类/i); expect(categories).toBeInTheDocument(); }); }); describe('Navigation', () => { it('should have case detail links', () => { render(); const links = screen.getAllByRole('link'); expect(links.length).toBeGreaterThan(0); }); }); describe('Accessibility', () => { it('should have main landmark', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should have proper heading hierarchy', () => { render(); const h1 = screen.getByRole('heading', { level: 1 }); expect(h1).toBeInTheDocument(); }); }); }); ``` **Step 3: 运行测试验证通过** Run: ```bash npm run test:unit -- src/app/\(marketing\)/cases/page.test.tsx ``` Expected: 所有测试通过 **Step 4: 提交代码** ```bash git add src/app/\(marketing\)/cases/page.test.tsx git commit -m "test: add cases page component tests" ``` --- ### Task 8: 验证Phase 1覆盖率 **Step 1: 运行测试覆盖率检查** Run: ```bash npm run test:unit -- --coverage --coverageReporters=text-summary ``` Expected: 覆盖率达到50%以上 **Step 2: 如果未达标,补充缺失测试** 根据覆盖率报告,补充缺失的测试用例。 **Step 3: 提交Phase 1完成标记** ```bash git add . git commit -m "feat: complete Phase 1 - test coverage reaches 50%" ``` --- ## Phase 2: 管理后台测试(提升到70%) ### Task 9: 管理后台登录页面测试 **Files:** - Create: `src/app/admin/login/page.test.tsx` - Test: `src/app/admin/login/page.tsx` **Step 1: 查看登录页面源码** Run: ```bash cat src/app/admin/login/page.tsx | head -100 ``` Expected: 显示登录页面结构和功能 **Step 2: 编写登录页面测试** Create: `src/app/admin/login/page.test.tsx` ```typescript import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import LoginPage from './page'; describe('LoginPage', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Rendering', () => { it('should render login page', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should render login form', () => { render(); const form = screen.getByRole('form'); expect(form).toBeInTheDocument(); }); it('should render email input', () => { render(); const emailInput = screen.getByLabelText(/邮箱/i); expect(emailInput).toBeInTheDocument(); }); it('should render password input', () => { render(); const passwordInput = screen.getByLabelText(/密码/i); expect(passwordInput).toBeInTheDocument(); }); it('should render login button', () => { render(); const loginButton = screen.getByRole('button', { name: /登录/i }); expect(loginButton).toBeInTheDocument(); }); }); describe('Form Validation', () => { it('should show error for empty email', async () => { render(); const loginButton = screen.getByRole('button', { name: /登录/i }); fireEvent.click(loginButton); await waitFor(() => { expect(screen.getByText(/请输入邮箱/i)).toBeInTheDocument(); }); }); it('should show error for invalid email', async () => { render(); const emailInput = screen.getByLabelText(/邮箱/i); fireEvent.change(emailInput, { target: { value: 'invalid-email' } }); const loginButton = screen.getByRole('button', { name: /登录/i }); fireEvent.click(loginButton); await waitFor(() => { expect(screen.getByText(/请输入有效的邮箱/i)).toBeInTheDocument(); }); }); it('should show error for empty password', async () => { render(); const emailInput = screen.getByLabelText(/邮箱/i); fireEvent.change(emailInput, { target: { value: 'admin@example.com' } }); const loginButton = screen.getByRole('button', { name: /登录/i }); fireEvent.click(loginButton); await waitFor(() => { expect(screen.getByText(/请输入密码/i)).toBeInTheDocument(); }); }); }); describe('Login Flow', () => { it('should login successfully with valid credentials', async () => { render(); const emailInput = screen.getByLabelText(/邮箱/i); const passwordInput = screen.getByLabelText(/密码/i); const loginButton = screen.getByRole('button', { name: /登录/i }); fireEvent.change(emailInput, { target: { value: 'admin@example.com' } }); fireEvent.change(passwordInput, { target: { value: 'password123' } }); fireEvent.click(loginButton); await waitFor(() => { expect(window.location.pathname).toBe('/admin'); }); }); it('should show error for invalid credentials', async () => { render(); const emailInput = screen.getByLabelText(/邮箱/i); const passwordInput = screen.getByLabelText(/密码/i); const loginButton = screen.getByRole('button', { name: /登录/i }); fireEvent.change(emailInput, { target: { value: 'wrong@example.com' } }); fireEvent.change(passwordInput, { target: { value: 'wrongpassword' } }); fireEvent.click(loginButton); await waitFor(() => { expect(screen.getByText(/邮箱或密码错误/i)).toBeInTheDocument(); }); }); }); describe('Accessibility', () => { it('should have main landmark', () => { render(); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); }); it('should have proper heading hierarchy', () => { render(); const h1 = screen.getByRole('heading', { level: 1 }); expect(h1).toBeInTheDocument(); }); it('should have accessible form labels', () => { render(); const emailInput = screen.getByLabelText(/邮箱/i); const passwordInput = screen.getByLabelText(/密码/i); expect(emailInput).toHaveAttribute('aria-label'); expect(passwordInput).toHaveAttribute('aria-label'); }); }); }); ``` **Step 3: 运行测试验证通过** Run: ```bash npm run test:unit -- src/app/admin/login/page.test.tsx ``` Expected: 所有测试通过 **Step 4: 提交代码** ```bash git add src/app/admin/login/page.test.tsx git commit -m "test: add admin login page tests" ``` --- ### Task 10-15: 管理后台其他页面测试 (类似Task 9,为管理后台的其他页面添加测试) - Task 10: 内容管理页面测试 - Task 11: 用户管理页面测试 - Task 12: 配置中心页面测试 - Task 13: 审计日志页面测试 - Task 14: 管理后台API测试 - Task 15: 验证Phase 2覆盖率 --- ## Phase 3: 建立长期质量保障机制 ### Task 16: 配置覆盖率门禁 **Files:** - Modify: `jest.config.js` - Modify: `.woodpecker/quality-gate.yml` **Step 1: 配置Jest覆盖率门禁** Modify: `jest.config.js` ```javascript coverageThreshold: { global: { branches: 70, functions: 70, lines: 70, statements: 70 } } ``` **Step 2: 配置CI/CD质量门禁** Modify: `.woodpecker/quality-gate.yml` ```yaml test:unit: image: node:20-alpine commands: - npm ci - npm run test:unit -- --coverage --coverageThreshold='{"global":{"branches":70,"functions":70,"lines":70,"statements":70}}' when: event: [push, pull_request] ``` **Step 3: 验证配置** Run: ```bash npm run test:unit -- --coverage ``` Expected: 如果覆盖率低于70%,测试失败 **Step 4: 提交配置** ```bash git add jest.config.js .woodpecker/quality-gate.yml git commit -m "feat: add coverage threshold gate (70%)" ``` --- ### Task 17: 建立测试规范文档 **Files:** - Create: `docs/TESTING_GUIDELINES.md` **Step 1: 创建测试规范文档** Create: `docs/TESTING_GUIDELINES.md` ```markdown # 测试规范指南 ## 测试策略 ### 测试金字塔 - 单元测试:70%(核心业务逻辑) - 集成测试:20%(API集成) - E2E测试:10%(关键用户流程) ### 覆盖率要求 - 全局覆盖率:≥70% - 核心模块覆盖率:≥90% - 新功能覆盖率:≥80% ## 测试命名规范 ### 测试文件命名 - 单元测试:`*.test.ts` 或 `*.test.tsx` - 集成测试:`*.integration.test.ts` - E2E测试:`*.spec.ts` ### 测试用例命名 使用描述性命名,遵循"应该..."模式: ```typescript describe('ComponentName', () => { describe('Feature', () => { it('should do something when condition', () => { // test code }); }); }); ``` ## 测试最佳实践 ### 1. AAA模式 - Arrange(准备):设置测试数据 - Act(执行):执行被测试的代码 - Assert(断言):验证结果 ### 2. 测试隔离 每个测试应该独立运行,不依赖其他测试: ```typescript beforeEach(() => { jest.clearAllMocks(); }); ``` ### 3. 测试可读性 - 使用有意义的变量名 - 避免魔法数字 - 添加必要的注释 ### 4. 测试覆盖率 - 关注代码路径覆盖 - 测试边界条件 - 测试错误处理 ## 运行测试 ### 单元测试 ```bash npm run test:unit ``` ### 覆盖率报告 ```bash npm run test:unit -- --coverage ``` ### E2E测试 ```bash npm run test:e2e ``` ## 持续集成 所有测试在CI/CD流水线中自动运行,覆盖率低于70%将导致构建失败。 ``` **Step 2: 提交文档** ```bash git add docs/TESTING_GUIDELINES.md git commit -m "docs: add testing guidelines" ``` --- ### Task 18: 团队培训材料 **Files:** - Create: `docs/TESTING_TRAINING.md` **Step 1: 创建培训材料** Create: `docs/TESTING_TRAINING.md` ```markdown # 测试培训材料 ## 培训目标 - 理解测试驱动开发(TDD) - 掌握Jest和React Testing Library - 编写高质量测试代码 ## 培训内容 ### 第一部分:测试基础 1. 测试的重要性 2. 测试类型(单元、集成、E2E) 3. 测试金字塔 ### 第二部分:Jest基础 1. Jest配置 2. 基本API(describe, it, expect) 3. Mock和Spy 4. 异步测试 ### 第三部分:React Testing Library 1. 渲染组件 2. 查询元素 3. 用户交互 4. 异步操作 ### 第四部分:最佳实践 1. AAA模式 2. 测试隔离 3. 可读性 4. 覆盖率 ## 实践练习 ### 练习1:编写第一个测试 为简单的工具函数编写测试。 ### 练习2:组件测试 为React组件编写测试。 ### 练习3:异步测试 测试异步操作(API调用)。 ## 参考资料 - [Jest官方文档](https://jestjs.io/) - [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) - [测试最佳实践](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library) ``` **Step 2: 提交培训材料** ```bash git add docs/TESTING_TRAINING.md git commit -m "docs: add testing training materials" ``` --- ### Task 19: 最终验证 **Step 1: 运行完整测试套件** Run: ```bash npm run test:unit -- --coverage --coverageReporters=text-summary ``` Expected: 覆盖率达到70%以上 **Step 2: 运行CI/CD流水线** Run: ```bash npm run lint && npm run typecheck && npm run test:unit ``` Expected: 所有检查通过 **Step 3: 生成最终报告** Run: ```bash npm run test:unit -- --coverage --coverageReporters=html ``` Expected: 生成HTML覆盖率报告 **Step 4: 提交最终完成标记** ```bash git add . git commit -m "feat: complete test coverage improvement to 70%" git tag v1.0.0-test-coverage-70 ``` --- ## 📊 预期成果 **Phase 1完成后:** - 测试覆盖率:≥50% - 页面组件测试:100%覆盖 - 核心业务功能:100%覆盖 **Phase 2完成后:** - 测试覆盖率:≥70% - 管理后台测试:100%覆盖 - API路由测试:100%覆盖 **Phase 3完成后:** - 覆盖率门禁:≥70% - 测试规范文档:完整 - 团队培训材料:完整 --- ## 🎯 成功标准 1. ✅ 测试覆盖率达到70%以上 2. ✅ 所有测试通过率100% 3. ✅ CI/CD质量门禁配置完成 4. ✅ 测试规范文档完整 5. ✅ 团队培训材料完整 --- **计划创建完成!**