README.md•4.02 kB
# Test Setup Documentation
## Overview
This test setup follows **DRY** (Don't Repeat Yourself), **KISS** (Keep It Simple, Stupid), and clean code principles to provide a maintainable and reliable testing environment.
## File Structure
```
src/test/
├── setup.ts # Jest lifecycle hooks and environment setup
├── helpers.ts # Test data creation and API helpers
├── utils.ts # Common utilities, assertions, and mocks
└── integration/
└── api.test.ts # API integration tests
```
## Key Improvements
### 1. **Unified Test Utilities** (`utils.ts`)
- **TestData**: Consistent data builders for projects and tasks
- **Assertions**: Common assertion helpers for date handling and data matching
- **MockUtils**: Centralized mock creation and management
- **TestEnv**: Environment utilities and configuration
### 2. **Clean Setup** (`setup.ts`)
- **Single Prisma Client**: One instance for all tests
- **Unified Cleanup**: Centralized database and cache cleanup
- **Error Handling**: Graceful error handling without stopping tests
- **Clear Lifecycle**: Well-defined beforeAll, afterAll, beforeEach hooks
### 3. **DRY Helpers** (`helpers.ts`)
- **Data Builders**: Reusable test data creation
- **Serialization**: Consistent date handling for cache compatibility
- **API Helpers**: Centralized API endpoint testing utilities
- **Type Safety**: Proper TypeScript types for all helpers
## Usage Examples
### Creating Test Data
```typescript
// Using TestData builders
const projectData = TestData.project({
name: 'Custom Project',
status: ProjectStatus.PLANNED
});
const taskData = TestData.task(projectId, {
title: 'Custom Task',
assignedTo: 'user@example.com'
});
```
### Making Assertions
```typescript
// Using Assertions helpers
Assertions.matchesData(actual, expected); // Excludes dates by default
Assertions.hasValidDates(obj, ['createdAt', 'updatedAt']);
Assertions.allHaveProperty(items, 'status', TaskStatus.TODO);
```
### Using Mocks
```typescript
// Using MockUtils
const cacheSpy = MockUtils.cacheSpy('get');
MockUtils.resetMocks();
```
## Best Practices
### 1. **Test Isolation**
- Each test creates its own data
- Database is cleaned between tests
- Cache is cleared between tests
- No shared state between tests
### 2. **Consistent Data**
- Use `TestData` builders for consistent test data
- Override only what you need to test
- Default values are realistic and reusable
### 3. **Reliable Assertions**
- Use `Assertions` helpers for common patterns
- Handle date comparisons properly
- Avoid brittle ID-based assertions
### 4. **Clean Mocks**
- Use `MockUtils` for consistent mocking
- Reset mocks between tests
- Mock only what you need to test
## Benefits
1. **Reduced Duplication**: Common patterns extracted to utilities
2. **Improved Maintainability**: Changes in one place affect all tests
3. **Better Reliability**: Consistent data and assertions
4. **Enhanced Readability**: Clear, descriptive helper functions
5. **Type Safety**: Full TypeScript support with proper types
## Migration Guide
### Before (Old Pattern)
```typescript
const projectData = {
name: 'Test Project',
description: 'Test Description',
startDate: new Date(),
endDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
status: ProjectStatus.IN_PROGRESS,
};
expect(project).toMatchObject({
name: projectData.name,
description: projectData.description,
status: projectData.status,
});
expect(project.startDate).toBeInstanceOf(Date);
expect(project.endDate).toBeInstanceOf(Date);
```
### After (New Pattern)
```typescript
const projectData = TestData.project({
name: 'Test Project',
description: 'Test Description',
status: ProjectStatus.IN_PROGRESS,
});
Assertions.matchesData(project, projectData);
Assertions.hasValidDates(project, ['startDate', 'endDate']);
```
This setup provides a clean, maintainable, and reliable testing foundation that follows software engineering best practices.