Skip to main content

backend-testing-guide

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

Quellinformationen

Repository
echoVic/boss-skill
Letzte Quellaktivität
29. September 2026 um 03:37
Erkannte Sprache von SKILL.md
Chinesisch
Sterne
562
Forks
51

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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',
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen