Files
antigravity-skills-reference/skills/backend-dev-guidelines/resources/testing-guide.md

236 lines
5.3 KiB
Markdown

# Testing Guide - Backend Testing Strategies
Complete guide to testing backend services with Jest and best practices.
## Table of Contents
- [Unit Testing](#unit-testing)
- [Integration Testing](#integration-testing)
- [Mocking Strategies](#mocking-strategies)
- [Test Data Management](#test-data-management)
- [Testing Authenticated Routes](#testing-authenticated-routes)
- [Coverage Targets](#coverage-targets)
---
## Unit Testing
### Test Structure
```typescript
// services/userService.test.ts
import { UserService } from './userService';
import { UserRepository } from '../repositories/UserRepository';
jest.mock('../repositories/UserRepository');
describe('UserService', () => {
let service: UserService;
let mockRepository: jest.Mocked<UserRepository>;
beforeEach(() => {
mockRepository = {
findByEmail: jest.fn(),
create: jest.fn(),
} as any;
service = new UserService();
(service as any).userRepository = mockRepository;
});
afterEach(() => {
jest.clearAllMocks();
});
describe('create', () => {
it('should throw error if email exists', async () => {
mockRepository.findByEmail.mockResolvedValue({ id: '123' } as any);
await expect(
service.create({ email: 'test@test.com' })
).rejects.toThrow('Email already in use');
});
it('should create user if email is unique', async () => {
mockRepository.findByEmail.mockResolvedValue(null);
mockRepository.create.mockResolvedValue({ id: '123' } as any);
const user = await service.create({
email: 'test@test.com',
firstName: 'John',
lastName: 'Doe',
});
expect(user).toBeDefined();
expect(mockRepository.create).toHaveBeenCalledWith(
expect.objectContaining({
email: 'test@test.com'
})
);
});
});
});
```
---
## Integration Testing
### Test with Real Database
```typescript
import { PrismaService } from '@project-lifecycle-portal/database';
describe('UserService Integration', () => {
let testUser: any;
beforeAll(async () => {
// Create test data
testUser = await PrismaService.main.user.create({
data: {
email: 'test@test.com',
profile: { create: { firstName: 'Test', lastName: 'User' } },
},
});
});
afterAll(async () => {
// Cleanup
await PrismaService.main.user.delete({ where: { id: testUser.id } });
});
it('should find user by email', async () => {
const user = await userService.findByEmail('test@test.com');
expect(user).toBeDefined();
expect(user?.email).toBe('test@test.com');
});
});
```
---
## Mocking Strategies
### Mock PrismaService
```typescript
jest.mock('@project-lifecycle-portal/database', () => ({
PrismaService: {
main: {
user: {
findMany: jest.fn(),
findUnique: jest.fn(),
create: jest.fn(),
update: jest.fn(),
},
},
isAvailable: true,
},
}));
```
### Mock Services
```typescript
const mockUserService = {
findById: jest.fn(),
create: jest.fn(),
update: jest.fn(),
} as jest.Mocked<UserService>;
```
---
## Test Data Management
### Setup and Teardown
```typescript
describe('PermissionService', () => {
let instanceId: number;
beforeAll(async () => {
// Create test post
const post = await PrismaService.main.post.create({
data: { title: 'Test Post', content: 'Test', authorId: 'test-user' },
});
instanceId = post.id;
});
afterAll(async () => {
// Cleanup
await PrismaService.main.post.delete({
where: { id: instanceId },
});
});
beforeEach(() => {
// Clear caches
permissionService.clearCache();
});
it('should check permissions', async () => {
const hasPermission = await permissionService.checkPermission(
'user-id',
instanceId,
'VIEW_WORKFLOW'
);
expect(hasPermission).toBeDefined();
});
});
```
---
## Testing Authenticated Routes
### Using test-auth-route.js
```bash
# Test authenticated endpoint
node scripts/test-auth-route.js http://localhost:3002/form/api/users
# Test with POST data
node scripts/test-auth-route.js http://localhost:3002/form/api/users POST '{"email":"test@test.com"}'
```
### Mock Authentication in Tests
```typescript
// Mock auth middleware
jest.mock('../middleware/SSOMiddleware', () => ({
SSOMiddlewareClient: {
verifyLoginStatus: (req, res, next) => {
res.locals.claims = {
sub: 'test-user-id',
preferred_username: 'testuser',
};
next();
},
},
}));
```
---
## Coverage Targets
### Recommended Coverage
- **Unit Tests**: 70%+ coverage
- **Integration Tests**: Critical paths covered
- **E2E Tests**: Happy paths covered
### Run Coverage
```bash
npm test -- --coverage
```
---
**Related Files:**
- [SKILL.md](SKILL.md)
- [services-and-repositories.md](services-and-repositories.md)
- [complete-examples.md](complete-examples.md)