---
title: 测试文档
order: 4
category: atom
---

# PisellCards 测试文档

## 测试框架

- **测试框架**：Jest
- **测试工具**：@testing-library/react
- **覆盖率要求**：80% 以上

## TabCard 测试用例

### 1. 基础渲染测试

#### 1.1 渲染基础 TabCard

```
it('应该正确渲染基础 TabCard', () => {
  const { container, getByText } = render(
    <TabCard tabKey="test" label="Test Tab" />
  );
  expect(getByText('Test Tab')).toBeInTheDocument();
  expect(container.querySelector('.tab-card')).toBeInTheDocument();
});
```

#### 1.2 渲染带图标的 TabCard

```
it('应该渲染带图标的 TabCard', () => {
  const { container } = render(
    <TabCard tabKey="test" label="Test" icon={<span>📱</span>} />
  );
  expect(container.querySelector('.tab-card__icon')).toBeInTheDocument();
});
```

#### 1.3 渲染带徽章的 TabCard

```
it('应该渲染带徽章的 TabCard', () => {
  const { container } = render(
    <TabCard tabKey="test" label="Test" badge={{ count: 5 }} />
  );
  expect(container.querySelector('.pisell-lowcode-badge')).toBeInTheDocument();
});
```

#### 1.4 快照测试

```
it('快照测试', () => {
  const { container } = render(<TabCard tabKey="test" label="Test" />);
  expect(container).toMatchSnapshot();
});
```

### 2. 状态测试

#### 2.1 激活状态

```
it('应该正确应用 active 状态', () => {
  const { container } = render(
    <TabCard tabKey="test" label="Test" active={{ value: true }} />
  );
  const tabCard = container.querySelector('.tab-card');
  expect(tabCard).toHaveClass('tab-card--active');
  expect(tabCard).toHaveAttribute('data-active', 'true');
  expect(tabCard).toHaveAttribute('aria-selected', 'true');
});
```

#### 2.2 禁用状态

```
it('应该正确应用 disabled 状态', () => {
  const { container } = render(
    <TabCard tabKey="test" label="Test" disabled={{ value: true }} />
  );
  const tabCard = container.querySelector('.tab-card');
  expect(tabCard).toHaveClass('tab-card--disabled');
  expect(tabCard).toHaveAttribute('data-disabled', 'true');
  expect(tabCard).toHaveAttribute('tabindex', '-1');
});
```

#### 2.3 加载状态

```
it('应该正确应用 loading 状态', () => {
  const { container } = render(
    <TabCard tabKey="test" label="Test" loading={{ value: true }} />
  );
  expect(container.querySelector('.tab-card')).toHaveClass('tab-card--loading');
});
```

#### 2.4 状态优先级

```
it('状态优先级：loading > disabled > active', () => {
  const { container } = render(
    <TabCard
      tabKey="test"
      label="Test"
      active={{ value: true, extra: <div>Active</div> }}
      disabled={{ value: true, extra: <div>Disabled</div> }}
      loading={{ value: true, extra: <div>Loading</div> }}
    />
  );
  expect(container.textContent).toContain('Loading');
  expect(container.textContent).not.toContain('Disabled');
  expect(container.textContent).not.toContain('Active');
});
```

### 3. 交互测试

#### 3.1 点击触发回调

```
it('点击应该触发 onClick 回调', () => {
  const onClick = jest.fn();
  const { container } = render(
    <TabCard tabKey="test" label="Test" onClick={onClick} />
  );
  const tabCard = container.querySelector('.tab-card') as HTMLElement;
  fireEvent.click(tabCard);
  expect(onClick).toHaveBeenCalledTimes(1);
  expect(onClick).toHaveBeenCalledWith('test', expect.anything());
});
```

#### 3.2 禁用状态不触发点击

```
it('disabled 状态下点击不应该触发回调', () => {
  const onClick = jest.fn();
  const { container } = render(
    <TabCard
      tabKey="test"
      label="Test"
      disabled={{ value: true }}
      onClick={onClick}
    />
  );
  const tabCard = container.querySelector('.tab-card') as HTMLElement;
  fireEvent.click(tabCard);
  expect(onClick).not.toHaveBeenCalled();
});
```

#### 3.3 active.onChange 触发

```
it('点击未激活的 TabCard 应该触发 active.onChange', () => {
  const onChange = jest.fn();
  const { container } = render(
    <TabCard tabKey="test" label="Test" active={{ value: false, onChange }} />
  );
  const tabCard = container.querySelector('.tab-card') as HTMLElement;
  fireEvent.click(tabCard);
  expect(onChange).toHaveBeenCalledWith(true);
});
```

### 4. 样式测试

#### 4.1 尺寸

```
it('应该正确应用 size 类名', () => {
  const { container: small } = render(
    <TabCard tabKey="test" label="Test" size="small" />
  );
  expect(small.querySelector('.tab-card')).toHaveClass('tab-card--small');

  const { container: large } = render(
    <TabCard tabKey="test" label="Test" size="large" />
  );
  expect(large.querySelector('.tab-card')).toHaveClass('tab-card--large');
});
```

#### 4.2 布局方向

```
it('应该正确应用 direction 类名', () => {
  const { container: horizontal } = render(
    <TabCard tabKey="test" label="Test" direction="horizontal" />
  );
  expect(horizontal.querySelector('.tab-card')).toHaveClass(
    'tab-card--horizontal'
  );

  const { container: vertical } = render(
    <TabCard tabKey="test" label="Test" direction="vertical" />
  );
  expect(vertical.querySelector('.tab-card')).toHaveClass('tab-card--vertical');
});
```

#### 4.3 自定义样式

```
it('自定义 className 和 style 应该生效', () => {
  const { container } = render(
    <TabCard
      tabKey="test"
      label="Test"
      className="custom"
      style={{ margin: '10px' }}
    />
  );
  const tabCard = container.querySelector('.tab-card');
  expect(tabCard).toHaveClass('custom');
  expect(tabCard).toHaveStyle({ margin: '10px' });
});
```

### 5. 自定义渲染测试

```
it('应该支持自定义 render 函数', () => {
  const customRender = (props: TabCardProps) => (
    <div className="custom">{props.label}</div>
  );
  const { container } = render(
    <TabCard tabKey="test" label="Test" render={customRender} />
  );
  expect(container.querySelector('.custom')).toBeInTheDocument();
});
```

### 6. 可访问性测试

```
it('应该有正确的 ARIA 属性', () => {
  const { container } = render(<TabCard tabKey="test" label="Test" />);
  const tabCard = container.querySelector('.tab-card');
  expect(tabCard).toHaveAttribute('role', 'tab');
  expect(tabCard).toHaveAttribute('data-tab-key', 'test');
  expect(tabCard).toHaveAttribute('tabindex', '0');
});
```

## 运行测试

### 运行所有测试

```bash
npm test
```

### 运行特定文件的测试

```bash
npm test TabCard.test.tsx
```

### 生成覆盖率报告

```bash
npm test -- --coverage
```

### Watch 模式

```bash
npm test -- --watch
```

## 测试覆盖率要求

| 类型       | 要求  |
| ---------- | ----- |
| 语句覆盖率 | ≥ 80% |
| 分支覆盖率 | ≥ 80% |
| 函数覆盖率 | ≥ 80% |
| 行覆盖率   | ≥ 80% |

## 新增卡片组件的测试清单

开发新卡片组件时，请确保包含以下测试：

- [ ] 基础渲染测试
- [ ] 快照测试
- [ ] active 状态测试
- [ ] disabled 状态测试
- [ ] loading 状态测试
- [ ] 状态优先级测试
- [ ] 点击事件测试
- [ ] disabled 状态下点击不触发
- [ ] loading 状态下点击不触发
- [ ] onChange 回调测试
- [ ] 样式和类名测试
- [ ] 自定义渲染测试
- [ ] 可访问性测试

## 测试最佳实践

### 1. 使用语义化查询

优先使用 `getByRole`、`getByLabelText` 等语义化查询方法。

```
// Good
const tab = getByRole('tab');

// Avoid
const tab = container.querySelector('.tab-card');
```

### 2. 测试用户行为

测试应该模拟真实的用户行为。

```
// Good
fireEvent.click(tabCard);

// Avoid
tabCard.click();
```

### 3. 避免测试实现细节

测试应该关注组件的行为，而非实现细节。

```
// Good
expect(tabCard).toHaveClass('tab-card--active');

// Avoid
expect(component.state.isActive).toBe(true);
```

### 4. 使用有意义的断言

断言应该清晰表达测试意图。

```
// Good
expect(onClick).toHaveBeenCalledWith('test', expect.anything());

// Avoid
expect(onClick).toHaveBeenCalled();
```

### 5. 保持测试独立

每个测试用例应该独立运行，不依赖其他测试。

```
// Good
it('should render', () => {
  const { container } = render(<TabCard tabKey="test" label="Test" />);
  // ...
});

// Avoid
let container;
it('should render', () => {
  container = render(<TabCard tabKey="test" label="Test" />).container;
});
it('should have class', () => {
  expect(container.querySelector('.tab-card')).toBeTruthy();
});
```
