Initial commit
This commit is contained in:
207
skills/testing-practices/SKILL.md
Normal file
207
skills/testing-practices/SKILL.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user