Files
2025-11-29 17:59:49 +08:00

208 lines
3.8 KiB
Markdown

# Testing Practices
**Comprehensive testing strategies across the stack**
## Testing Pyramid
### Unit Tests (70%)
- Test individual functions/methods
- Fast execution
- No external dependencies
- High coverage of business logic
### Integration Tests (20%)
- Test component interactions
- Database queries
- API endpoints
- Service integrations
### E2E Tests (10%)
- Test user workflows
- Full stack testing
- Browser automation
- Critical paths only
## Frontend Testing
### Component Testing (React)
```tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'
describe('Button', () => {
it('calls onClick when clicked', () => {
const onClick = jest.fn()
render(<Button onClick={onClick}>Click me</Button>)
fireEvent.click(screen.getByText('Click me'))
expect(onClick).toHaveBeenCalledTimes(1)
})
})
```
### State Management Testing
```tsx
import { renderHook, act } from '@testing-library/react-hooks'
import { useCounter } from './useCounter'
it('increments counter', () => {
const { result } = renderHook(() => useCounter())
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})
```
### E2E Testing (Playwright)
```typescript
import { test, expect } from '@playwright/test'
test('user can log in', async ({ page }) => {
await page.goto('/login')
await page.fill('[name=email]', 'user@example.com')
await page.fill('[name=password]', 'password123')
await page.click('button[type=submit]')
await expect(page).toHaveURL('/dashboard')
await expect(page.locator('h1')).toContainText('Welcome')
})
```
## Backend Testing
### API Testing
```typescript
import request from 'supertest'
import { app } from './app'
describe('POST /api/users', () => {
it('creates a new user', async () => {
const res = await request(app)
.post('/api/users')
.send({
name: 'John Doe',
email: 'john@example.com'
})
.expect(201)
expect(res.body).toHaveProperty('id')
expect(res.body.email).toBe('john@example.com')
})
})
```
### Database Testing
```typescript
beforeEach(async () => {
await db.migrate.rollback()
await db.migrate.latest()
await db.seed.run()
})
afterEach(async () => {
await db.destroy()
})
```
## Test Best Practices
### AAA Pattern
```typescript
it('example test', () => {
// Arrange
const input = { ... }
// Act
const result = functionUnderTest(input)
// Assert
expect(result).toBe(expected)
})
```
### Test Isolation
- Each test should be independent
- No shared state between tests
- Clean up after each test
- Use beforeEach/afterEach appropriately
### Meaningful Test Names
```typescript
// ❌ BAD
it('works', () => { ... })
// ✅ GOOD
it('returns 404 when user not found', () => { ... })
```
### Mocking
```typescript
jest.mock('./api')
it('handles API errors', async () => {
api.fetchUser.mockRejectedValue(new Error('Network error'))
// Test error handling
})
```
## Performance Testing
### Load Testing
- Apache JMeter
- k6
- Artillery
### Stress Testing
- Identify breaking points
- Test recovery
- Monitor resource usage
## Test Coverage
### Coverage Metrics
- Line coverage: 80%+ target
- Branch coverage: 75%+ target
- Function coverage: 90%+ target
### Coverage Tools
- Jest (--coverage)
- Istanbul/nyc
- Codecov/Coveralls
## CI/CD Integration
### GitHub Actions
```yaml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install
- run: npm test
- run: npm run test:e2e
```
### Test Stages
1. Lint and type check
2. Unit tests
3. Integration tests
4. Build verification
5. E2E tests (on staging)
## Debugging Tests
- Use `test.only` to isolate
- Add console.logs strategically
- Use debugger statements
- Check test environment setup
- Verify mock implementations