docs: add comprehensive testing documentation
Add detailed testing guides and implementation documentation: TESTING.md (560 lines): - Complete testing guide for developers - Backend testing with Jest, Supertest, MongoDB Memory Server - Frontend testing with React Testing Library, MSW - Test writing examples and patterns - Coverage requirements and reporting - CI/CD integration guidelines - Best practices and conventions TESTING_QUICK_START.md (250 lines): - Quick reference for running tests - Common test commands (backend and frontend) - Test file locations and organization - Coverage report viewing - Troubleshooting common issues - Quick examples for common scenarios TEST_IMPLEMENTATION_SUMMARY.md (750 lines): - Detailed implementation report - Coverage statistics by area - Test file descriptions - Known issues and future improvements - Architecture decisions - Test infrastructure details - MSW handler documentation CLAUDE.md Updates: - Add testing section with quick commands - Document test coverage targets - Reference comprehensive testing docs - Add Kubernetes deployment section for Raspberry Pi cluster - Document cluster configuration (2x Pi 5 8GB, 1x Pi 3B+ 1GB) - ARM64 and ARMv7 architecture considerations - Resource optimization strategies Key Documentation Features: - Step-by-step guides for writing tests - Examples for all test types (unit, integration, E2E) - Coverage reporting and analysis - Debugging test failures - Performance testing guidelines - Mock data management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
581
TESTING.md
Normal file
581
TESTING.md
Normal file
@@ -0,0 +1,581 @@
|
||||
# Testing Documentation
|
||||
|
||||
Comprehensive testing infrastructure for the Adopt-a-Street application.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Backend Testing](#backend-testing)
|
||||
3. [Frontend Testing](#frontend-testing)
|
||||
4. [Running Tests](#running-tests)
|
||||
5. [Coverage Reports](#coverage-reports)
|
||||
6. [Test Structure](#test-structure)
|
||||
7. [Best Practices](#best-practices)
|
||||
8. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Overview
|
||||
|
||||
The Adopt-a-Street application uses a comprehensive testing strategy covering both backend and frontend:
|
||||
|
||||
- **Backend**: Jest + Supertest + MongoDB Memory Server
|
||||
- **Frontend**: React Testing Library + Jest + MSW (Mock Service Worker)
|
||||
- **Current Coverage**:
|
||||
- Backend: ~55% statement coverage (109 passing tests)
|
||||
- Frontend: MSW infrastructure in place with component tests
|
||||
|
||||
## Backend Testing
|
||||
|
||||
### Test Infrastructure
|
||||
|
||||
The backend uses:
|
||||
- **Jest**: Test runner and assertion library
|
||||
- **Supertest**: HTTP assertions for API endpoint testing
|
||||
- **MongoDB Memory Server**: In-memory MongoDB for isolated testing
|
||||
- **Cross-env**: Environment variable management
|
||||
|
||||
### Setup Files
|
||||
|
||||
#### `backend/jest.config.js`
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
testEnvironment: 'node',
|
||||
coverageDirectory: 'coverage',
|
||||
collectCoverageFrom: [
|
||||
'routes/**/*.js',
|
||||
'middleware/**/*.js',
|
||||
'models/**/*.js',
|
||||
'!**/node_modules/**',
|
||||
'!**/coverage/**'
|
||||
],
|
||||
testMatch: [
|
||||
'**/__tests__/**/*.test.js',
|
||||
'**/?(*.)+(spec|test).js'
|
||||
],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 70,
|
||||
functions: 70,
|
||||
lines: 70,
|
||||
statements: 70
|
||||
}
|
||||
},
|
||||
setupFilesAfterEnv: ['<rootDir>/__tests__/setup.js'],
|
||||
testTimeout: 30000,
|
||||
verbose: true
|
||||
};
|
||||
```
|
||||
|
||||
#### `backend/__tests__/setup.js`
|
||||
|
||||
Sets up MongoDB Memory Server for all tests:
|
||||
- Creates in-memory MongoDB instance before all tests
|
||||
- Clears collections after each test
|
||||
- Closes connections after all tests
|
||||
- Suppresses console logs during tests
|
||||
|
||||
### Test Files
|
||||
|
||||
#### Route Tests (`__tests__/routes/`)
|
||||
|
||||
- `auth.test.js` - Authentication endpoints (register, login, get user)
|
||||
- `streets.test.js` - Street CRUD operations and adoption
|
||||
- `tasks.test.js` - Task management operations
|
||||
- `posts.test.js` - Social feed post operations
|
||||
- `events.test.js` - Community event management
|
||||
- `rewards.test.js` - Points and rewards system
|
||||
- `reports.test.js` - Street condition reporting
|
||||
|
||||
#### Model Tests (`__tests__/models/`)
|
||||
|
||||
- `User.test.js` - User model validation and relationships
|
||||
- `Street.test.js` - Street model with GeoJSON location
|
||||
- `Task.test.js` - Task model validation
|
||||
- `Post.test.js` - Post model with likes and comments
|
||||
|
||||
#### Middleware Tests (`__tests__/middleware/`)
|
||||
|
||||
- `auth.test.js` - JWT authentication middleware
|
||||
|
||||
#### Test Helpers (`__tests__/utils/`)
|
||||
|
||||
`testHelpers.js` provides utilities for:
|
||||
- Creating test users with JWT tokens
|
||||
- Creating test streets, tasks, posts, events, rewards, reports
|
||||
- Database cleanup utilities
|
||||
|
||||
### Example Backend Test
|
||||
|
||||
```javascript
|
||||
const request = require('supertest');
|
||||
const express = require('express');
|
||||
const authRoutes = require('../../routes/auth');
|
||||
const { createTestUser } = require('../utils/testHelpers');
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use('/api/auth', authRoutes);
|
||||
|
||||
describe('Auth Routes', () => {
|
||||
it('should register a new user', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send({
|
||||
name: 'John Doe',
|
||||
email: 'john@example.com',
|
||||
password: 'password123',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).toHaveProperty('token');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Frontend Testing
|
||||
|
||||
### Test Infrastructure
|
||||
|
||||
The frontend uses:
|
||||
- **React Testing Library**: Component testing utilities
|
||||
- **Jest**: Test runner (via react-scripts)
|
||||
- **MSW (Mock Service Worker)**: API mocking
|
||||
- **@testing-library/user-event**: User interaction simulation
|
||||
|
||||
### Setup Files
|
||||
|
||||
#### `frontend/src/setupTests.js`
|
||||
|
||||
- Configures Jest-DOM matchers
|
||||
- Sets up MSW server for API mocking
|
||||
- Mocks localStorage and window.matchMedia
|
||||
- Suppresses expected console warnings
|
||||
|
||||
#### `frontend/src/mocks/handlers.js`
|
||||
|
||||
Defines MSW handlers for all API endpoints:
|
||||
- Authentication (register, login, get user)
|
||||
- Streets (list, adopt)
|
||||
- Tasks (list, create, complete)
|
||||
- Posts (list, create, like)
|
||||
- Events (list, create, RSVP)
|
||||
- Rewards (list, redeem)
|
||||
- Users (get, update)
|
||||
|
||||
### Test Files
|
||||
|
||||
#### Component Tests (`src/components/__tests__/`)
|
||||
|
||||
- `Login.test.js` - Login form validation and submission
|
||||
- `Register.test.js` - Registration form validation
|
||||
- `ErrorBoundary.test.js` - Error handling component
|
||||
|
||||
#### Integration Tests (`src/__tests__/`)
|
||||
|
||||
- `auth-flow.integration.test.js` - Complete authentication flows
|
||||
|
||||
### Example Frontend Test
|
||||
|
||||
```javascript
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import Login from '../Login';
|
||||
import { AuthContext } from '../../context/AuthContext';
|
||||
|
||||
describe('Login Component', () => {
|
||||
it('should render login form', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<AuthContext.Provider value={mockContext}>
|
||||
<Login />
|
||||
</AuthContext.Provider>
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
expect(screen.getByPlaceholderText(/email/i)).toBeInTheDocument();
|
||||
expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Backend Tests
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Run tests in watch mode
|
||||
npm run test:watch
|
||||
|
||||
# Run tests with coverage
|
||||
npm run test:coverage
|
||||
|
||||
# Run tests with verbose output
|
||||
npm run test:verbose
|
||||
```
|
||||
|
||||
### Frontend Tests
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
|
||||
# Run all tests (interactive watch mode)
|
||||
npm test
|
||||
|
||||
# Run tests with coverage
|
||||
npm run test:coverage
|
||||
|
||||
# Run tests in watch mode
|
||||
npm run test:watch
|
||||
```
|
||||
|
||||
### Run All Tests
|
||||
|
||||
From the project root:
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend && npm test
|
||||
|
||||
# Frontend
|
||||
cd frontend && npm test
|
||||
```
|
||||
|
||||
## Coverage Reports
|
||||
|
||||
### Backend Coverage
|
||||
|
||||
Current coverage (as of last run):
|
||||
|
||||
```
|
||||
-----------------------|---------|----------|---------|---------|
|
||||
File | % Stmts | % Branch | % Funcs | % Lines |
|
||||
-----------------------|---------|----------|---------|---------|
|
||||
All files | 54.75 | 32.23 | 62.66 | 54.85 |
|
||||
middleware | 40.24 | 21.05 | 45.45 | 39.5 |
|
||||
auth.js | 100 | 100 | 100 | 100 |
|
||||
models | 82.5 | 66.66 | 50 | 82.5 |
|
||||
routes | 45.84 | 22.61 | 60.97 | 46.07 |
|
||||
-----------------------|---------|----------|---------|---------|
|
||||
```
|
||||
|
||||
**Test Results**: 109 passing, 67 failing (schema mismatches)
|
||||
|
||||
Key Coverage Areas:
|
||||
- ✅ Auth middleware: 100% coverage
|
||||
- ✅ Model validators: 82.5% coverage
|
||||
- ⚠️ Routes: 45.84% coverage (room for improvement)
|
||||
- ⚠️ Other middleware: 40.24% coverage
|
||||
|
||||
### Frontend Coverage
|
||||
|
||||
Coverage configuration in `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
"branches": 50,
|
||||
"functions": 60,
|
||||
"lines": 60,
|
||||
"statements": 60
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Coverage reports are generated in:
|
||||
- Backend: `backend/coverage/`
|
||||
- Frontend: `frontend/coverage/`
|
||||
|
||||
To view HTML coverage reports:
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend && npm test -- --coverage
|
||||
open coverage/lcov-report/index.html
|
||||
|
||||
# Frontend
|
||||
cd frontend && npm run test:coverage
|
||||
open coverage/lcov-report/index.html
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
### Backend Test Organization
|
||||
|
||||
```
|
||||
backend/
|
||||
├── __tests__/
|
||||
│ ├── setup.js # Global test setup
|
||||
│ ├── middleware/
|
||||
│ │ └── auth.test.js # Middleware tests
|
||||
│ ├── models/
|
||||
│ │ ├── User.test.js # Model tests
|
||||
│ │ ├── Street.test.js
|
||||
│ │ ├── Task.test.js
|
||||
│ │ └── Post.test.js
|
||||
│ ├── routes/
|
||||
│ │ ├── auth.test.js # Route tests
|
||||
│ │ ├── streets.test.js
|
||||
│ │ ├── tasks.test.js
|
||||
│ │ ├── posts.test.js
|
||||
│ │ ├── events.test.js
|
||||
│ │ ├── rewards.test.js
|
||||
│ │ └── reports.test.js
|
||||
│ └── utils/
|
||||
│ └── testHelpers.js # Test utilities
|
||||
└── jest.config.js # Jest configuration
|
||||
```
|
||||
|
||||
### Frontend Test Organization
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── setupTests.js # Global test setup
|
||||
├── mocks/
|
||||
│ ├── handlers.js # MSW API handlers
|
||||
│ └── server.js # MSW server setup
|
||||
├── __tests__/
|
||||
│ └── auth-flow.integration.test.js # Integration tests
|
||||
└── components/
|
||||
└── __tests__/
|
||||
├── Login.test.js # Component tests
|
||||
├── Register.test.js
|
||||
└── ErrorBoundary.test.js
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Backend Testing
|
||||
|
||||
1. **Use Test Helpers**: Leverage `testHelpers.js` for consistent test data creation
|
||||
2. **Isolate Tests**: Each test should be independent and not rely on other tests
|
||||
3. **Mock External Services**: Mock Cloudinary, Stripe, and other external APIs
|
||||
4. **Test Error Cases**: Don't just test happy paths, test error handling
|
||||
5. **Use Descriptive Names**: Test names should clearly describe what they test
|
||||
6. **Clean Up**: The test setup automatically cleans up between tests
|
||||
|
||||
### Frontend Testing
|
||||
|
||||
1. **Test User Behavior**: Focus on how users interact with components
|
||||
2. **Use MSW for API Mocking**: Don't mock axios directly, use MSW handlers
|
||||
3. **Test Accessibility**: Use semantic queries (getByRole, getByLabelText)
|
||||
4. **Avoid Implementation Details**: Test what users see, not component internals
|
||||
5. **Use waitFor for Async**: Always use waitFor for asynchronous operations
|
||||
6. **Mock External Libraries**: Mock Leaflet, Socket.IO, and other heavy libraries
|
||||
|
||||
### Writing New Tests
|
||||
|
||||
#### Backend Route Test Template
|
||||
|
||||
```javascript
|
||||
const request = require('supertest');
|
||||
const express = require('express');
|
||||
const yourRoute = require('../../routes/your-route');
|
||||
const { createTestUser } = require('../utils/testHelpers');
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use('/api/your-route', yourRoute);
|
||||
|
||||
describe('Your Route', () => {
|
||||
describe('GET /api/your-route', () => {
|
||||
it('should do something', async () => {
|
||||
const { token } = await createTestUser();
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/your-route')
|
||||
.set('x-auth-token', token)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).toMatchObject({ /* expected shape */ });
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
#### Frontend Component Test Template
|
||||
|
||||
```javascript
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import YourComponent from '../YourComponent';
|
||||
|
||||
describe('YourComponent', () => {
|
||||
it('should render and work', async () => {
|
||||
render(<YourComponent />);
|
||||
|
||||
const button = screen.getByRole('button', { name: /submit/i });
|
||||
fireEvent.click(button);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/success/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### MongoDB Memory Server Timeout
|
||||
|
||||
If tests timeout with MongoDB Memory Server:
|
||||
|
||||
```bash
|
||||
# Increase timeout in jest.config.js
|
||||
testTimeout: 30000 // 30 seconds
|
||||
```
|
||||
|
||||
#### MSW Not Intercepting Requests
|
||||
|
||||
Ensure MSW server is set up in `setupTests.js`:
|
||||
|
||||
```javascript
|
||||
import { server } from './mocks/server';
|
||||
|
||||
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
|
||||
afterEach(() => server.resetHandlers());
|
||||
afterAll(() => server.close());
|
||||
```
|
||||
|
||||
#### React Router Errors in Tests
|
||||
|
||||
Wrap components with BrowserRouter:
|
||||
|
||||
```javascript
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<YourComponent />
|
||||
</BrowserRouter>
|
||||
);
|
||||
```
|
||||
|
||||
#### Auth Context Not Found
|
||||
|
||||
Provide AuthContext in tests:
|
||||
|
||||
```javascript
|
||||
import { AuthContext } from '../../context/AuthContext';
|
||||
|
||||
const mockAuthContext = {
|
||||
auth: { isAuthenticated: false, loading: false, user: null },
|
||||
login: jest.fn(),
|
||||
};
|
||||
|
||||
render(
|
||||
<AuthContext.Provider value={mockAuthContext}>
|
||||
<YourComponent />
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
```
|
||||
|
||||
#### Leaflet Map Errors
|
||||
|
||||
Mock react-leaflet in tests:
|
||||
|
||||
```javascript
|
||||
jest.mock('react-leaflet', () => ({
|
||||
MapContainer: ({ children }) => <div data-testid="map">{children}</div>,
|
||||
TileLayer: () => null,
|
||||
Marker: () => null,
|
||||
Popup: () => null,
|
||||
}));
|
||||
```
|
||||
|
||||
### Performance Tips
|
||||
|
||||
1. **Run specific test files**:
|
||||
```bash
|
||||
npm test -- auth.test.js
|
||||
```
|
||||
|
||||
2. **Run tests matching pattern**:
|
||||
```bash
|
||||
npm test -- --testNamePattern="login"
|
||||
```
|
||||
|
||||
3. **Skip tests during development**:
|
||||
```javascript
|
||||
describe.skip('Skip this suite', () => { /* ... */ });
|
||||
it.skip('Skip this test', () => { /* ... */ });
|
||||
```
|
||||
|
||||
4. **Run only specific tests**:
|
||||
```javascript
|
||||
describe.only('Run only this suite', () => { /* ... */ });
|
||||
it.only('Run only this test', () => { /* ... */ });
|
||||
```
|
||||
|
||||
## Future Improvements
|
||||
|
||||
### Backend
|
||||
|
||||
- [ ] Increase route coverage to 70%+
|
||||
- [ ] Add tests for Socket.IO events
|
||||
- [ ] Add tests for file upload (Multer)
|
||||
- [ ] Add tests for AI route
|
||||
- [ ] Add tests for payments route
|
||||
- [ ] Add performance/load tests
|
||||
- [ ] Add E2E tests with real database
|
||||
|
||||
### Frontend
|
||||
|
||||
- [ ] Add tests for all remaining components:
|
||||
- MapView
|
||||
- TaskList
|
||||
- SocialFeed
|
||||
- Events
|
||||
- Profile
|
||||
- Premium
|
||||
- Rewards
|
||||
- [ ] Add integration tests for complete user flows
|
||||
- [ ] Add accessibility tests (jest-axe)
|
||||
- [ ] Add visual regression tests (Percy/Chromatic)
|
||||
- [ ] Add E2E tests (Playwright/Cypress)
|
||||
- [ ] Increase coverage to 60%+
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
To integrate tests into CI/CD pipeline:
|
||||
|
||||
### GitHub Actions Example
|
||||
|
||||
```yaml
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
backend-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- run: cd backend && npm install
|
||||
- run: cd backend && npm test -- --coverage
|
||||
|
||||
frontend-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- run: cd frontend && npm install
|
||||
- run: cd frontend && npm run test:coverage
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [Jest Documentation](https://jestjs.io/)
|
||||
- [React Testing Library](https://testing-library.com/react)
|
||||
- [Supertest](https://github.com/visionmedia/supertest)
|
||||
- [MSW Documentation](https://mswjs.io/)
|
||||
- [MongoDB Memory Server](https://github.com/nodkz/mongodb-memory-server)
|
||||
- [Testing Best Practices](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library)
|
||||
Reference in New Issue
Block a user