Skip to main content

backend-testing-guide

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

소스 정보

저장소
echoVic/boss-skill
최근 소스 활동
2026년 9월 29일 03:37
감지된 SKILL.md 언어
중국어
스타
563
포크
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에서 보기