Skip to main content

backend-testing-guide

后端测试编写指南,包括单元测试、集成测试和E2E测试的编写方法和最佳实践

ソース情報

リポジトリ
echoVic/boss-skill
ソースの最終更新活動
2026年9月29日 03:37
検出された SKILL.md の言語
中国語
スター
562
フォーク
51

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
backend/testing-guide
description
后端测试编写指南,包括单元测试、集成测试和E2E测试的编写方法和最佳实践
type
methodology
agent
boss-backend
metadata
{"internal":true}
# 后端测试编写指南 ## 测试要求(强制) > **职责边界**:Backend Agent 是测试的**编写者**,QA Agent 是测试的**验证者**。 ### 测试金字塔 | 测试类型 | 占比 | 要求 | |----------|------|------| | **单元测试** | ~70% | Service 层、业务逻辑必须有测试 | | **集成测试** | ~20% | API 端点、数据库操作测试 | | **E2E 测试** | ~10% | **必须编写**,完整 API 流程测试 | ## 单元测试编写 ### Service 层测试 ```typescript // services/userService.test.ts import { UserService } from './userService'; import { UserRepository } from '../repositories/userRepository'; jest.mock('../repositories/userRepository'); describe('UserService', () => { let userService: UserService; let userRepository: jest.Mocked<UserRepository>; beforeEach(() => { userRepository = new UserRepository() as jest.Mocked<UserRepository>; userService = new UserService(userRepository); }); describe('getById', () => { it('returns user when found', async () => { const mockUser = { id: '1', name: 'Alice', email: 'alice@example.com' }; userRepository.findById.mockResolvedValue(mockUser); const result = await userService.getById('1'); expect(result).toEqual(mockUser); expect(userRepository.findById).toHaveBeenCalledWith('1'); }); it('throws NotFoundError when user not found', async () => { userRepository.findById.mockResolvedValue(null); await expect(userService.getById('999')).rejects.toThrow('User not found'); }); }); describe('create', () => { it('creates user with valid data', async () => { const createData = { name: 'Bob', email: 'bob@example.com' }; const mockUser = { id: '2', ...createData }; userRepository.findByEmail.mockResolvedValue(null); userRepository.create.mockResolvedValue(mockUser); const result = await userService.create(createData); expect(result).toEqual(mockUser); expect(userRepository.findByEmail).toHaveBeenCalledWith('bob@example.com'); expect(userRepository.create).toHaveBeenCalledWith(createData); }); it('throws ConflictError when email already exists', async () => { const createData = { name: 'Bob', email: 'existing@example.com' }; userRepository.findByEmail.mockResolvedValue({ id: '1', name: 'Existing', email: 'existing@example.com' }); await expect(userService.create(createData)).rejects.toThrow('Email already exists'); }); }); }); ``` ### 业务逻辑测试 ```typescript // services/orderService.test.ts describe('OrderService', () => { describe('calculateTotal', () => { it('calculates total with discount', () => { const items = [ { price: 100, quantity: 2 }, { price: 50, quantity: 1 }, ]; const discount = 0.1; // 10% off const total = orderService.calculateTotal(items, discount); expect(total).toBe(225); // (200 + 50) * 0.9 }); it('handles zero discount', () => { const items = [{ price: 100, quantity: 1 }]; const total = orderService.calculateTotal(items, 0); expect(total).toBe(100); }); }); describe('validateOrder', () => { it('validates order with sufficient stock', async () => { const order = { productId: '1', quantity: 5 }; productRepository.findById.mockResolvedValue({ id: '1', stock: 10 }); const result = await orderService.validateOrder(order); expect(result.valid).toBe(true); }); it('rejects order with insufficient stock', async () => { const order = { productId: '1', quantity: 15 }; productRepository.findById.mockResolvedValue({ id: '1', stock: 10 }); const result = await orderService.validateOrder(order); expect(result.valid).toBe(false); expect(result.error).toBe('Insufficient stock'); }); }); }); ``` ## 集成测试编写 ### API 端点测试 ```typescript // controllers/userController.test.ts import request from 'supertest'; import { app } from '../app'; import { db } from '../db'; describe('User API', () => { beforeEach(async () => { // 清理测试数据库 await db.user.deleteMany(); }); afterAll(async () => { await db.$disconnect(); }); describe('POST /api/users', () => { it('creates a new user', async () => { const userData = { name: 'Alice', email: 'alice@example.com', }; const response = await request(app) .post('/api/users') .send(userData) .expect(201); expect(response.body.success).toBe(true); expect(response.body.data).toMatchObject(userData); expect(response.body.data.id).toBeDefined(); }); it('returns 400 for invalid email', async () => { const userData = { name: 'Bob', email: 'invalid-email', }; const response = await request(app) .post('/api/users') .send(userData) .expect(400); expect(response.body.success).toBe(false); expect(response.body.error.code).toBe('VALIDATION_ERROR'); }); it('returns 409 for duplicate email', async () => { const userData = { name: 'Charlie', email: 'duplicate@example.com', }; // 创建第一个用户 await request(app).post('/api/users').send(userData); // 尝试创建重复邮箱的用户 const response = await request(app) .post('/api/users') .send(userData) .expect(409); expect(response.body.error.code).toBe('CONFLICT'); }); }); describe('GET /api/users/:id', () => { it('returns user by id', async () => { // 创建测试用户 const createResponse = await request(app) .post('/api/users') .send({ name: 'Dave', email: 'dave@example.com' }); const userId = createResponse.body.data.id; // 获取用户 const response = await request(app) .get(`/api/users/${userId}`) .expect(200); expect(response.body.data.id).toBe(userId); expect(response.body.data.name).toBe('Dave'); }); it('returns 404 for non-existent user', async () => { const response = await request(app) .get('/api/users/non-existent-id') .expect(404); expect(response.body.error.code).toBe('NOT_FOUND'); }); }); describe('PUT /api/users/:id', () => { it('updates user', async () => { // 创建用户 const createResponse = await request(app) .post('/api/users') .send({ name: 'Eve', email: 'eve@example.com' }); const userId = createResponse.body.data.id; // 更新用户 const response = await request(app) .put(`/api/users/${userId}`) .send({ name: 'Eve Updated' }) .expect(200); expect(response.body.data.name).toBe('Eve Updated'); expect(response.body.data.email).toBe('eve@example.com'); }); }); describe('DELETE /api/users/:id', () => { it('deletes user', async () => { // 创建用户 const createResponse = await request(app) .post('/api/users') .send({ name: 'Frank', email: 'frank@example.com' }); const userId = createResponse.body.data.id; // 删除用户 await request(app) .delete(`/api/users/${userId}`) .expect(200); // 验证用户已删除 await request(app) .get(`/api/users/${userId}`) .expect(404); }); }); }); ``` ### 数据库操作测试 ```typescript // repositories/userRepository.test.ts import { UserRepository } from './userRepository'; import { db } from '../db'; describe('UserRepository', () => { let userRepository: UserRepository; beforeEach(async () => { userRepository = new UserRepository(); await db.user.deleteMany(); }); afterAll(async () => { await db.$disconnect(); }); describe('create', () => { it('creates user in database', async () => { const userData = { name: 'Alice', email: 'alice@example.com' }; const user = await userRepository.create(userData); expect(user.id).toBeDefined(); expect(user.name).toBe('Alice'); // 验证数据库中存在 const found = await db.user.findUnique({ where: { id: user.id } }); expect(found).toBeTruthy(); }); }); describe('findByEmail', () => { it('finds user by email', async () => { await userRepository.create({ name: 'Bob', email: 'bob@example.com' }); const user = await userRepository.findByEmail('bob@example.com'); expect(user).toBeTruthy(); expect(user?.name).toBe('Bob'); }); it('returns null for non-existent email', async () => { const user = await userRepository.findByEmail('nonexistent@example.com'); expect(user).toBeNull(); }); }); }); ``` ## E2E 测试编写(必须) ### API E2E 测试必须覆盖 - ✅ 创建资源(POST) - ✅ 读取资源(GET) - ✅ 更新资源(PUT/PATCH) - ✅ 删除资源(DELETE) - ✅ 完整业务流程(如:注册→登录→操作) ### 完整 CRUD 流程测试 ```typescript // e2e/user-crud.test.ts import request from 'supertest'; import { app } from '../app'; import { db } from '../db'; describe('User CRUD E2E', () => { beforeAll(async () => { await db.user.deleteMany(); }); afterAll(async () => { await db.$disconnect(); }); it('completes full user lifecycle', async () => { // 1. 创建用户 const createResponse = await request(app) .post('/api/users') .send({ name: 'John Doe', email: 'john@example.com',
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る