# unit测试js

## 规则（Rules）

# JS单元测试规范

## 适用对象和范围

本规范适用于所有使用Jest/Mocha对JavaScript/Node.js代码进行单元测试的场景。

---

## 1. 测试框架规范

**规则**：JS单元测试必须使用Jest框架（推荐）或Mocha框架。

- ✅ 正确：`npm install --save-dev jest`
- ❌ 错误：使用非标准测试框架

**违反后果**：非标准框架可能导致社区支持不足，CI/CD集成困难。

---

## 2. 测试结构规范

**规则**：必须使用 `describe` 和 `it` 组织测试结构。

```javascript
describe('模块名', () => {
  it('测试描述', () => {
    // 测试逻辑
  });
});
```

- ✅ 正确：`describe('UserService', () => { it('should get user by id', () => { ... }) })`
- ❌ 错误：直接写 `test()` 或 `it()` 没有分组

**违反后果**：无分组的测试难以阅读和维护，不清楚测试属于哪个模块。

---

## 3. 测试函数命名规范

**规则**：测试函数名使用小驼峰（camelCase），描述清晰。

- ✅ 正确：`testGetUserData()`、`shouldReturnUserWhenIdExists()`
- ❌ 错误：`test1()`、`test_user_data()`

**违反后果**：测试命名不清晰导致无法从名称判断测试内容。

---

## 4. 断言规范

**规则**：每个测试用例必须有明确的断言，禁止无断言的测试。

- ✅ 正确：`expect(result).toEqual(expected);`
- ❌ 错误：`await getUserData(1);`（无断言）

**违反后果**：无断言的测试无法验证代码正确性，形同虚设。

---

## 5. 覆盖率规范

**规则**：行覆盖率必须达到80%以上。

- ✅ 正确：运行 `npx jest --coverage` 检查覆盖率
- ❌ 错误：覆盖率低于80%就提交代码

**违反后果**：覆盖率不足导致未测试的代码可能存在bug。

---

## 6. 测试文件命名规范

**规则**：测试文件名必须使用 `test_{模块名}.js` 格式。

- ✅ 正确：`test_user_service.js`、`test_user_controller.js`
- ❌ 错误：`user_service.test.js`、`userService.spec.js`

**违反后果**：测试文件命名不一致导致查找困难。

## 方法（Methods）

# JS单元测试方法

## 前置条件

- [ ] 已确认被测代码文件路径
- [ ] 已安装Jest测试框架

## 流程概览

分析代码结构 → 编写单元测试用例 → 执行测试并获取覆盖率 → 生成测试报告 → 回归测试（代码修改后）

## 详细步骤

### 步骤1：分析代码结构
搜索JS代码文件，读取每个文件提取导出函数。

### 步骤2：编写单元测试用例
对每个函数编写测试用例，覆盖：
- 正常路径：输入有效数据，期望返回正确结果
- 边界值：输入边界数据（空值、最大值、最小值等）
- 异常路径：输入非法数据，期望抛出异常

## 技巧（Tips）

# JS单元测试技巧

## 1. 使用describe嵌套组织测试

**适用场景**：需要按功能模块组织大量测试用例时。

**具体做法**：使用嵌套的 `describe` 层级组织测试。

**示例**：
```javascript
describe('UserService', () => {
  describe('getUserData', () => {
    it('should return user when id exists', async () => {
      const user = await UserService.getUserData(1);
      expect(user).toBeDefined();
      expect(user.id).toBe(1);
    });
    
    it('should throw error when id not exists', async () => {
      await expect(UserService.getUserData(999))
        .rejects.toThrow('用户不存在');
    });
  });
  
  describe('createUser', () => {
    it('should create user with valid data', async () => { ... });
    it('should reject duplicate email', async () => { ... });
  });
});
```

**注意事项**：嵌套层级不超过3层，否则可读性下降。

---

## 2. 使用beforeEach准备测试数据

**适用场景**：多个测试用例需要相同的测试数据时。

**具体做法**：使用 `beforeEach` 在每个测试前初始化数据。

**对比说明**：

| 维度 | 每个用例单独准备 | beforeEach统一准备 |
|------|----------------|------------------|
| 代码量 | 重复代码多 | 一次定义，多处复用 |
| 维护性 | 修改数据需改多处 | 只改一处 |
| 用例隔离 | 容易互相影响 | 每次重新初始化 |

**示例**：
```javascript
describe('UserService', () => {
  let testUser;
  
  beforeEach(() => {
    testUser = { id: 1, name: '张三', email: 'zhang@test.com' };
  });
  
  it('should get user by id', async () => {
    // 使用 testUser
  });
});
```

**注意事项**：`beforeEach` 在每个 `it` 前都会执行，确保用例间数据隔离。

---

## 3. 使用mock模拟外部依赖

**适用场景**：测试依赖数据库、网络请求等外部服务的函数时。

**具体做法**：使用Jest的 `jest.mock()` 或 `jest.fn()` 模拟外部依赖。

**示例**：
```javascript
// 模拟数据库查询
jest.mock('../models/user_model', () => ({
  findById: jest.fn()
}));

const UserModel = require('../models/user_model');

it('should return formatted user data', async () => {
  UserModel.findById.mockResolvedValue({
    id: 1,
    name: '张三',
    email: 'zhang@test.com'
  });
  
  const result = await UserService.getUserData(1);
  expect(result.name).toBe('张三');
  expect(UserModel.findById).toHaveBeenCalledWith(1);
});
```

**注意事项**：mock应该在测试文件顶部声明，`clearAllMocks` 在 `afterEach` 中调用。

---

## 4. 常见问题速查

| 问题 | 原因 | 解决方案 |
|------|------|---------|
| 测试超时 | 异步测试未返回Promise | 确保 `it` 的回调返回Promise或使用 `async/await` |
| mock未生效 | 导入顺序问题 | 在 `require` 被测试模块前先 `jest.mock` |
| 覆盖率不达标 | 未测试分支逻辑 | 补充if/else、try/catch等分支的测试 |
| 测试间相互影响 | 共享可变数据 | 使用 `beforeEach` 重新初始化数据 |
| 测试运行缓慢 | 包含真实网络请求 | 使用mock模拟网络请求 |
