# 测试指南

## 测试策略

### 测试金字塔

```
        ┌───────┐
        │  E2E  │     少量端到端测试
       ┌┴───────┴┐
       │ Integration│   API和组件集成测试
      ┌┴──────────┴┐
      │   Unit     │    大量单元测试
     ┌┴────────────┴┐
```

### 测试覆盖目标

| 层级 | 覆盖率目标 |
|------|-----------|
| 单元测试 | 80%+ |
| 集成测试 | 60%+ |
| E2E测试 | 关键流程覆盖 |

## 后端测试

### 目录结构

```
tests/
├── conftest.py          # pytest配置和fixture
├── unit/               # 单元测试
│   ├── services/
│   └── models/
├── integration/        # 集成测试
│   └── api/
└── fixtures/           # 测试数据
```

### 运行测试

```bash
# 运行所有测试
pytest

# 运行单元测试
pytest tests/unit/

# 运行集成测试
pytest tests/integration/

# 带覆盖率
pytest --cov=app --cov-report=html
```

### 编写单元测试

```python
import pytest
from app.services.user_service import UserService
from app.models import User

@pytest.fixture
def user_service(db_session):
    return UserService(session=db_session)

@pytest.fixture
def sample_user():
    return User(
        id=1,
        email="test@example.com",
        username="testuser"
    )

def test_get_user_by_id(user_service, sample_user, db_session):
    db_session.add(sample_user)
    db_session.commit()
    
    result = user_service.get_user_by_id(1)
    
    assert result is not None
    assert result.email == "test@example.com"
```

### Fixtures

常用Fixtures（`conftest.py`）：

```python
@pytest.fixture
def db_session():
    """数据库会话"""
    ...

@pytest.fixture
def client():
    """测试客户端"""
    ...

@pytest.fixture
def auth_headers():
    """认证头"""
    ...
```

## 前端测试

### 工具链

- Vitest - 单元测试
- React Testing Library - 组件测试
- Playwright - E2E测试

### 运行测试

```bash
# 单元测试
npm run test

# E2E测试
npm run test:e2e

# 覆盖率
npm run test:coverage
```

### 组件测试示例

```typescript
import { render, screen, userEvent } from '@testing-library/react';
import { Button } from './Button';

describe('Button', () => {
  it('renders with label', () => {
    render(<Button label="Click me" onClick={() => {}} />);
    expect(screen.getByText('Click me')).toBeInTheDocument();
  });

  it('calls onClick when clicked', async () => {
    const onClick = vi.fn();
    render(<Button label="Click me" onClick={onClick} />);
    
    await userEvent.click(screen.getByText('Click me'));
    
    expect(onClick).toHaveBeenCalled();
  });
});
```

## E2E测试

使用Playwright进行端到端测试：

```typescript
import { test, expect } from '@playwright/test';

test.describe('登录流程', () => {
  test('应该能够登录并跳转到dashboard', async ({ page }) => {
    await page.goto('/login');
    
    await page.fill('[name="email"]', 'test@example.com');
    await page.fill('[name="password"]', 'password123');
    await page.click('[type="submit"]');
    
    await expect(page).toHaveURL('/dashboard');
    await expect(page.locator('h1')).toContainText('品牌健康中心');
  });
});
```

## CI集成

测试在每次PR时自动运行：

```yaml
# .github/workflows/test.yml
name: Test

on: [pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: |
          docker-compose up -d
          pytest
          npm run test:e2e
```
