Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
9.8 KiB
Testing Documentation
Comprehensive testing guide for the Telegram Marketing Agent System.
Table of Contents
- Testing Strategy
- Test Types
- Running Tests
- Writing Tests
- CI/CD Integration
- Coverage Goals
- Best Practices
Testing Strategy
Our testing strategy follows the Testing Pyramid approach:
/\
/E2E\ (5-10%)
/------\
/Integration\ (20-30%)
/------------\
/ Unit Tests \ (60-70%)
/-----------------\
Test Categories
- Unit Tests: Test individual functions, methods, and components in isolation
- Integration Tests: Test interactions between components and services
- End-to-End Tests: Test complete user workflows and scenarios
Test Types
Unit Tests
Located in tests/unit/, these tests cover:
- Service methods
- Utility functions
- Middleware logic
- Model validations
- Helper functions
Example structure:
tests/unit/
├── services/
│ ├── api-gateway/
│ │ ├── middleware/
│ │ │ ├── auth.test.js
│ │ │ └── rateLimiter.test.js
│ │ └── utils/
│ └── orchestrator/
│ └── campaignService.test.js
└── utils/
Integration Tests
Located in tests/integration/, these tests cover:
- API endpoint functionality
- Database operations
- Service interactions
- External API mocking
Example structure:
tests/integration/
├── api/
│ ├── auth.test.js
│ ├── campaigns.test.js
│ └── users.test.js
└── services/
End-to-End Tests
Located in tests/e2e/, these tests cover:
- Complete user workflows
- Multi-service interactions
- Real-world scenarios
Example structure:
tests/e2e/
├── campaigns/
│ └── campaignWorkflow.test.js
└── users/
└── userOnboarding.test.js
Running Tests
Prerequisites
# Install dependencies
npm install
# For each service
cd services/<service-name>
npm install
All Tests
npm test
Unit Tests Only
npm run test:unit
Integration Tests Only
npm run test:integration
E2E Tests Only
npm run test:e2e
Watch Mode (for development)
npm run test:watch
Coverage Report
npm run test:coverage
Specific Test File
npx jest tests/unit/services/orchestrator/campaignService.test.js
Test Pattern
npx jest --testNamePattern="should create campaign"
Writing Tests
Unit Test Example
import { jest } from '@jest/globals';
import CampaignService from '../../../../services/orchestrator/src/services/campaignService.js';
import { createCampaign } from '../../../helpers/factories.js';
describe('CampaignService', () => {
let campaignService;
let mockDependency;
beforeEach(() => {
// Setup mocks
mockDependency = {
method: jest.fn()
};
campaignService = new CampaignService(mockDependency);
jest.clearAllMocks();
});
describe('createCampaign', () => {
it('should create a new campaign', async () => {
// Arrange
const campaignData = createCampaign();
mockDependency.method.mockResolvedValue({ success: true });
// Act
const result = await campaignService.createCampaign(campaignData);
// Assert
expect(result).toHaveProperty('id');
expect(mockDependency.method).toHaveBeenCalledWith(expect.any(Object));
});
it('should handle errors gracefully', async () => {
// Arrange
mockDependency.method.mockRejectedValue(new Error('Database error'));
// Act & Assert
await expect(campaignService.createCampaign({}))
.rejects.toThrow('Database error');
});
});
});
Integration Test Example
import request from 'supertest';
import app from '../../../services/api-gateway/src/app.js';
import { connectDatabase, closeDatabase, clearDatabase } from '../../helpers/database.js';
describe('Campaigns API', () => {
let authToken;
beforeAll(async () => {
await connectDatabase();
authToken = await getAuthToken();
});
afterEach(async () => {
await clearDatabase();
});
afterAll(async () => {
await closeDatabase();
});
describe('POST /api/v1/campaigns', () => {
it('should create campaign', async () => {
const response = await request(app)
.post('/api/v1/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send({
name: 'Test Campaign',
type: 'message'
});
expect(response.status).toBe(201);
expect(response.body.success).toBe(true);
expect(response.body.data.campaign).toHaveProperty('id');
});
});
});
E2E Test Example
describe('Campaign Workflow', () => {
it('should complete full campaign lifecycle', async () => {
// 1. Create template
const template = await createTemplate();
// 2. Import users
const users = await importUsers();
// 3. Create segment
const segment = await createSegment();
// 4. Create campaign
const campaign = await createCampaign({
templateId: template.id,
segmentId: segment.id
});
// 5. Execute campaign
const execution = await executeCampaign(campaign.id);
// 6. Verify results
expect(execution.status).toBe('completed');
expect(execution.messagesSent).toBe(users.length);
});
});
Test Helpers and Utilities
Database Helpers
import { connectDatabase, closeDatabase, clearDatabase } from './helpers/database.js';
Factory Functions
import {
createUser,
createCampaign,
createTemplate
} from './helpers/factories.js';
Authentication Helpers
import { generateAuthToken, createAuthenticatedRequest } from './helpers/auth.js';
CI/CD Integration
Tests run automatically on:
- Pull requests
- Commits to main/develop branches
- Before deployments
GitHub Actions Workflow
See .github/workflows/test.yml for the complete CI configuration.
Key features:
- Matrix testing (Node.js 18.x, 20.x)
- Multiple database versions
- Parallel test execution
- Coverage reporting
- Test result artifacts
Pre-commit Hooks
# Install husky
npm prepare
# Pre-commit hook runs:
- Linting
- Unit tests for changed files
- Commit message validation
Coverage Goals
Overall Coverage Targets
- Statements: 80%
- Branches: 70%
- Functions: 70%
- Lines: 80%
Service-Specific Targets
- API Gateway: 85% (critical path)
- Orchestrator: 80%
- Analytics: 75%
- User Management: 80%
- Scheduler: 75%
Viewing Coverage
# Generate HTML coverage report
npm run test:coverage
# Open in browser
open coverage/lcov-report/index.html
Best Practices
General Guidelines
-
Test Naming: Use descriptive test names that explain what is being tested
// Good it('should return 404 when campaign does not exist') // Bad it('test campaign') -
Arrange-Act-Assert: Structure tests clearly
it('should calculate discount correctly', () => { // Arrange const price = 100; const discountRate = 0.2; // Act const result = calculateDiscount(price, discountRate); // Assert expect(result).toBe(80); }); -
Isolation: Each test should be independent
- Use
beforeEachandafterEachfor setup/cleanup - Don't rely on test execution order
- Clear mocks between tests
- Use
-
Mocking: Mock external dependencies
jest.mock('axios'); axios.get.mockResolvedValue({ data: mockData }); -
Async Testing: Handle promises properly
// Good it('should handle async operation', async () => { await expect(asyncFunction()).resolves.toBe(expected); }); // Also good it('should handle async operation', () => { return expect(asyncFunction()).resolves.toBe(expected); }); -
Error Testing: Test error cases thoroughly
it('should throw error for invalid input', async () => { await expect(functionUnderTest(null)) .rejects.toThrow('Input cannot be null'); });
Performance Considerations
- Use Test Databases: MongoDB Memory Server for unit tests
- Parallel Execution: Run independent tests in parallel
- Selective Testing: Use
--watchmode during development - Mock Heavy Operations: Mock file I/O, network calls
Security Testing
- Authentication: Test all auth scenarios
- Authorization: Verify role-based access
- Input Validation: Test with malicious inputs
- Rate Limiting: Verify limits are enforced
Troubleshooting
Common Issues
-
Timeout Errors
// Increase timeout for specific test it('should handle long operation', async () => { // test code }, 10000); // 10 second timeout -
Database Connection Issues
- Ensure MongoDB/Redis are running
- Check connection strings in test environment
- Clear test database between runs
-
Flaky Tests
- Add proper waits for async operations
- Mock time-dependent functions
- Use stable test data
-
Memory Leaks
- Close all connections in
afterAll - Clear large data structures
- Use
--detectLeaksflag
- Close all connections in