- Prettier 配置迁移为 .prettierrc.cjs,解决 ESM/CJS 兼容问题 - 优化 package.json,补全元信息、整理依赖、完善 Bun 热更新脚本 - 归档项目初始化 PRD 与任务清单到 tasks/archive,并加日期前缀 - 同步代码风格与格式化配置,提升团队协作一致性 归档文件:tasks/archive/20240610-prd-项目初始化.md, tasks/archive/20240610-tasks-prd-项目初始化.md
100 lines
3.4 KiB
Markdown
100 lines
3.4 KiB
Markdown
# Elysia + Vitest 测试用例编写规范
|
||
|
||
## 1. 文件结构与命名
|
||
|
||
- 每个被测模块应有对应的 `xxx.test.ts` 文件,放在同目录或 `tests/` 下。
|
||
- 测试文件需有文件头注释,说明作者、日期、描述等。
|
||
|
||
## 2. 测试用例基本格式
|
||
|
||
- 使用 `describe` 分组,`it`(或 `test`)描述单个场景。
|
||
- 每个 `it` 需有明确的行为描述。
|
||
|
||
## 3. 参数传递与类型
|
||
|
||
- 明确传递参数类型,推荐用 TypeBox schema 自动推导类型。
|
||
- 测试用例中参数应覆盖正常、异常、边界值。
|
||
|
||
## 4. 响应结构与 code 校验
|
||
|
||
- 断言 HTTP 状态码(如 200、400、401)。
|
||
- 断言响应体结构(如 code、message、data),类型可用 `as any` 或 schema 类型断言。
|
||
|
||
## 5. 边界与异常测试
|
||
|
||
- 必须覆盖参数边界(如最短/最长用户名、最短密码等)。
|
||
- 必须覆盖异常分支(如缺少参数、token 错误、未授权等)。
|
||
|
||
## 6. 类型安全
|
||
|
||
- 推荐用 TypeBox 的 `Static<typeof schema>` 推导类型,或在测试用例中用 `as any` 简化断言。
|
||
- 对于复杂响应,可定义类型辅助断言。
|
||
|
||
## 7. 断言范围
|
||
|
||
- 断言应覆盖:HTTP 状态码、响应 code、message、data 字段、关键数据类型。
|
||
- 对于 token、id 等动态值,用 `typeof` 或正则断言。
|
||
|
||
## 8. 其他建议
|
||
|
||
- 用 `it.only`/`describe.only` 聚焦调试,提交前移除。
|
||
- 用 `console.log` 输出实际响应,便于排查失败。
|
||
- 每个接口的正常、异常、边界场景都应有测试覆盖。
|
||
|
||
## 9. 示例
|
||
|
||
```typescript
|
||
/**
|
||
* @file 用户登录接口测试
|
||
* @author hotok
|
||
* @date 2025-06-28
|
||
* @lastEditor hotok
|
||
* @lastEditTime 2025-06-28
|
||
* @description 覆盖登录接口的正常、异常、边界场景
|
||
*/
|
||
|
||
describe('POST /api/login', () => {
|
||
it('应登录成功并返回token', async () => {
|
||
const res = await app.fetch(
|
||
new Request('http://localhost/api/login', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ username: 'admin', password: '123456' }),
|
||
}),
|
||
);
|
||
const body = (await res.json()) as any;
|
||
expect(res.status).toBe(200);
|
||
expect(body.code).toBe(0);
|
||
expect(typeof body.data.token).toBe('string');
|
||
});
|
||
|
||
it('用户名过短应返回400', async () => {
|
||
const res = await app.fetch(
|
||
new Request('http://localhost/api/login', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ username: 'a', password: '123456' }),
|
||
}),
|
||
);
|
||
const body = (await res.json()) as any;
|
||
expect(res.status).toBe(400);
|
||
expect(body.code).toBe(400);
|
||
expect(body.message).toMatch(/用户名长度/);
|
||
});
|
||
|
||
it('密码错误应返回400', async () => {
|
||
const res = await app.fetch(
|
||
new Request('http://localhost/api/login', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ username: 'admin', password: 'wrong' }),
|
||
}),
|
||
);
|
||
const body = (await res.json()) as any;
|
||
expect(res.status).toBe(400);
|
||
expect(body.code).toBe(400);
|
||
expect(body.message).toBe('用户名或密码错误');
|
||
});
|
||
});
|
||
```
|