- Add 15 comprehensive skills extracted from Windsurf workflows - Cover debugging, testing, releases, deployment, security, and documentation - Each skill includes step-by-step instructions and copy-pasteable commands - Skills organized by category with cross-references and difficulty levels
510 lines
11 KiB
Markdown
510 lines
11 KiB
Markdown
# Test Strategies Skill
|
|
|
|
**Description**: Comprehensive testing patterns and strategies for full-stack applications.
|
|
|
|
## When to Use
|
|
|
|
- Writing new tests
|
|
- Setting up testing infrastructure
|
|
- Improving test coverage
|
|
- Debugging test failures
|
|
|
|
## Testing Pyramid
|
|
|
|
```
|
|
E2E Tests (10%)
|
|
─────────────────
|
|
Integration Tests (20%)
|
|
─────────────────────────
|
|
Unit Tests (70%)
|
|
```
|
|
|
|
## Unit Testing
|
|
|
|
### Python (pytest)
|
|
|
|
```bash
|
|
# Run all tests
|
|
python -m pytest tests/ -v
|
|
|
|
# Run specific test file
|
|
python -m pytest tests/test_auth.py -v
|
|
|
|
# Run with coverage
|
|
python -m pytest tests/ --cov=src --cov-report=html
|
|
|
|
# Run failing tests first
|
|
python -m pytest tests/ --lf
|
|
|
|
# Stop on first failure
|
|
python -m pytest tests/ -x
|
|
```
|
|
|
|
#### Test Structure
|
|
|
|
```python
|
|
# tests/test_auth.py
|
|
import pytest
|
|
from src.auth import auth_client
|
|
|
|
class TestAuthClient:
|
|
def setup_method(self):
|
|
"""Run before each test"""
|
|
self.client = auth_client.AuthClient()
|
|
|
|
def test_login_success(self):
|
|
"""Test successful login"""
|
|
result = self.client.login("user@example.com", "password")
|
|
assert result.success is True
|
|
assert result.token is not None
|
|
|
|
def test_login_failure(self):
|
|
"""Test login with wrong password"""
|
|
result = self.client.login("user@example.com", "wrong")
|
|
assert result.success is False
|
|
assert "Invalid credentials" in result.error
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_login(self):
|
|
"""Test async login method"""
|
|
result = await self.client.async_login("user@example.com", "password")
|
|
assert result.success is True
|
|
```
|
|
|
|
### TypeScript (Vitest)
|
|
|
|
```bash
|
|
# Run all tests
|
|
pnpm test
|
|
|
|
# Run in watch mode
|
|
pnpm test --watch
|
|
|
|
# Run with coverage
|
|
pnpm test --coverage
|
|
|
|
# Run specific test file
|
|
pnpm test auth.test.ts
|
|
```
|
|
|
|
#### Test Structure
|
|
|
|
```typescript
|
|
// src/modules/auth/__tests__/auth.test.ts
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { AuthService } from '../service';
|
|
|
|
describe('AuthService', () => {
|
|
let service: AuthService;
|
|
|
|
beforeEach(() => {
|
|
service = new AuthService();
|
|
});
|
|
|
|
it('should validate JWT token', async () => {
|
|
const token = 'valid.jwt.token';
|
|
const result = await service.validateToken(token);
|
|
expect(result.valid).toBe(true);
|
|
expect(result.userId).toBe('user123');
|
|
});
|
|
|
|
it('should reject invalid token', async () => {
|
|
const token = 'invalid.token';
|
|
const result = await service.validateToken(token);
|
|
expect(result.valid).toBe(false);
|
|
});
|
|
});
|
|
```
|
|
|
|
## Integration Testing
|
|
|
|
### API Integration Tests
|
|
|
|
```python
|
|
# tests/test_api_integration.py
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
from src.main import app
|
|
|
|
client = TestClient(app)
|
|
|
|
def test_user_flow():
|
|
"""Test complete user registration and login flow"""
|
|
# Register user
|
|
response = client.post("/api/auth/register", json={
|
|
"email": "test@example.com",
|
|
"password": "password123"
|
|
})
|
|
assert response.status_code == 201
|
|
|
|
# Login
|
|
response = client.post("/api/auth/login", json={
|
|
"email": "test@example.com",
|
|
"password": "password123"
|
|
})
|
|
assert response.status_code == 200
|
|
token = response.json()["token"]
|
|
|
|
# Access protected endpoint
|
|
response = client.get("/api/user/profile", headers={
|
|
"Authorization": f"Bearer {token}"
|
|
})
|
|
assert response.status_code == 200
|
|
assert response.json()["email"] == "test@example.com"
|
|
```
|
|
|
|
### Database Integration Tests
|
|
|
|
```typescript
|
|
// src/modules/__tests__/integration.test.ts
|
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import { CosmosClient } from '@azure/cosmos';
|
|
import { UserRepository } from '../users/repository';
|
|
|
|
describe('UserRepository Integration', () => {
|
|
let client: CosmosClient;
|
|
let repo: UserRepository;
|
|
|
|
beforeAll(async () => {
|
|
client = new CosmosClient(process.env.COSMOS_ENDPOINT!);
|
|
repo = new UserRepository(client);
|
|
});
|
|
|
|
it('should create and retrieve user', async () => {
|
|
const user = {
|
|
id: 'test-user',
|
|
email: 'test@example.com',
|
|
productId: 'lysnrai',
|
|
};
|
|
|
|
await repo.create(user);
|
|
const retrieved = await repo.getById('test-user');
|
|
|
|
expect(retrieved).toBeDefined();
|
|
expect(retrieved.email).toBe(user.email);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// Cleanup test data
|
|
await repo.delete('test-user');
|
|
});
|
|
});
|
|
```
|
|
|
|
## End-to-End Testing
|
|
|
|
### Playwright (Web Dashboards)
|
|
|
|
```bash
|
|
# Install Playwright
|
|
npm install -D @playwright/test
|
|
|
|
# Install browsers
|
|
npx playwright install
|
|
|
|
# Run E2E tests
|
|
npm run test:e2e
|
|
|
|
# Run in headed mode (useful for debugging)
|
|
npm run test:e2e -- --headed
|
|
```
|
|
|
|
#### Test Structure
|
|
|
|
```typescript
|
|
// e2e/login.spec.ts
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Authentication', () => {
|
|
test('user can login', async ({ page }) => {
|
|
await page.goto('/login');
|
|
|
|
await page.fill('[data-testid=email]', 'admin@example.com');
|
|
await page.fill('[data-testid=password]', 'password');
|
|
await page.click('[data-testid=login-button]');
|
|
|
|
// Should redirect to dashboard
|
|
await expect(page).toHaveURL('/dashboard');
|
|
await expect(page.locator('h1')).toContainText('Welcome');
|
|
});
|
|
|
|
test('shows error for invalid credentials', async ({ page }) => {
|
|
await page.goto('/login');
|
|
|
|
await page.fill('[data-testid=email]', 'admin@example.com');
|
|
await page.fill('[data-testid=password]', 'wrong');
|
|
await page.click('[data-testid=login-button]');
|
|
|
|
await expect(page.locator('[data-testid=error]')).toContainText('Invalid credentials');
|
|
});
|
|
});
|
|
```
|
|
|
|
### Mobile E2E Tests
|
|
|
|
```typescript
|
|
// mobile/e2e/app.test.ts
|
|
import { device, element, by, expect } from 'detox';
|
|
|
|
describe('Mobile App', () => {
|
|
beforeAll(async () => {
|
|
await device.launchApp();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
await device.reloadReactNative();
|
|
});
|
|
|
|
it('should show welcome screen', async () => {
|
|
await expect(element(by.id('welcome-screen'))).toBeVisible();
|
|
});
|
|
|
|
it('should navigate to settings', async () => {
|
|
await element(by.id('settings-button')).tap();
|
|
await expect(element(by.id('settings-screen'))).toBeVisible();
|
|
});
|
|
});
|
|
```
|
|
|
|
## Test Data Management
|
|
|
|
### Fixtures (Python)
|
|
|
|
```python
|
|
# tests/conftest.py
|
|
import pytest
|
|
from src.database import get_db_session
|
|
|
|
@pytest.fixture
|
|
def db_session():
|
|
"""Provide a database session for tests"""
|
|
session = get_db_session()
|
|
yield session
|
|
session.rollback()
|
|
session.close()
|
|
|
|
@pytest.fixture
|
|
def sample_user():
|
|
"""Provide a sample user for tests"""
|
|
return {
|
|
"id": "test-user-123",
|
|
"email": "test@example.com",
|
|
"name": "Test User",
|
|
"productId": "lysnrai"
|
|
}
|
|
```
|
|
|
|
### Mock Data (TypeScript)
|
|
|
|
```typescript
|
|
// src/__tests__/mocks/data.ts
|
|
export const mockUser = {
|
|
id: 'user-123',
|
|
email: 'test@example.com',
|
|
createdAt: new Date().toISOString(),
|
|
productId: 'lysnrai',
|
|
};
|
|
|
|
export const mockSubscription = {
|
|
id: 'sub-123',
|
|
userId: 'user-123',
|
|
status: 'active',
|
|
planId: 'pro',
|
|
};
|
|
```
|
|
|
|
## Testing Best Practices
|
|
|
|
### 1. Test Naming
|
|
|
|
```python
|
|
# Good: descriptive and clear
|
|
def test_user_login_with_valid_credentials_returns_token()
|
|
|
|
# Bad: vague
|
|
def test_login()
|
|
```
|
|
|
|
### 2. AAA Pattern (Arrange, Act, Assert)
|
|
|
|
```python
|
|
def test_discount_applied():
|
|
# Arrange
|
|
cart = ShoppingCart()
|
|
cart.add_item("book", 10)
|
|
coupon = Coupon("DISCOUNT10", 0.1)
|
|
|
|
# Act
|
|
cart.apply_coupon(coupon)
|
|
|
|
# Assert
|
|
assert cart.total == 9.00
|
|
```
|
|
|
|
### 3. Test Isolation
|
|
|
|
```python
|
|
# Each test should be independent
|
|
def test_create_user():
|
|
user = User(name="Alice")
|
|
assert user.name == "Alice"
|
|
|
|
def test_update_user():
|
|
user = User(name="Bob") # Fresh instance, not from previous test
|
|
user.update_name("Charlie")
|
|
assert user.name == "Charlie"
|
|
```
|
|
|
|
### 4. Use Test Doubles
|
|
|
|
```typescript
|
|
// Mock external dependencies
|
|
vi.mock('@azure/cosmos', () => ({
|
|
CosmosClient: vi.fn().mockImplementation(() => ({
|
|
database: vi.fn().mockReturnValue({
|
|
container: vi.fn().mockReturnValue({
|
|
items: {
|
|
create: vi.fn().mockResolvedValue({ resource: mockUser })
|
|
}
|
|
})
|
|
})
|
|
})
|
|
}))
|
|
```
|
|
|
|
## Coverage Requirements
|
|
|
|
### Coverage Targets
|
|
|
|
| Component | Unit Tests | Integration | E2E | Total Coverage |
|
|
| ------------------- | ---------- | ----------- | --- | -------------- |
|
|
| Backend Services | 80% | 40% | 10% | 85% |
|
|
| Frontend Components | 70% | 30% | 20% | 80% |
|
|
| Shared Libraries | 90% | 50% | 0% | 90% |
|
|
| Mobile Apps | 70% | 30% | 30% | 80% |
|
|
|
|
### Coverage Reports
|
|
|
|
```bash
|
|
# Python
|
|
coverage html
|
|
open htmlcov/index.html
|
|
|
|
# TypeScript
|
|
open coverage/lcov-report/index.html
|
|
```
|
|
|
|
## CI/CD Integration
|
|
|
|
### GitHub Actions
|
|
|
|
```yaml
|
|
name: Tests
|
|
on: [push, pull_request]
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
python-version: [3.12]
|
|
node-version: [20]
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v4
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: ${{ matrix.node-version }}
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
pip install -e ".[dev]"
|
|
npm install
|
|
|
|
- name: Run tests
|
|
run: |
|
|
python -m pytest tests/ --cov=src
|
|
npm run test --coverage
|
|
|
|
- name: Upload coverage
|
|
uses: codecov/codecov-action@v3
|
|
```
|
|
|
|
## Debugging Tests
|
|
|
|
### Python
|
|
|
|
```bash
|
|
# Run with debugger
|
|
python -m pytest tests/test_file.py --pdb
|
|
|
|
# Print output
|
|
python -m pytest tests/ -v -s
|
|
|
|
# Show local variables on failure
|
|
python -m pytest tests/ --tb=long
|
|
```
|
|
|
|
### TypeScript
|
|
|
|
```bash
|
|
# Run in debug mode
|
|
pnpm test --inspect-brk
|
|
|
|
# Run specific test file with debugger
|
|
pnpm test specific.test.ts --inspect-brk
|
|
```
|
|
|
|
## Performance Testing
|
|
|
|
### Load Testing (k6)
|
|
|
|
```javascript
|
|
// tests/load/api.js
|
|
import http from 'k6/http';
|
|
import { check } from 'k6';
|
|
|
|
export let options = {
|
|
stages: [
|
|
{ duration: '2m', target: 100 },
|
|
{ duration: '5m', target: 100 },
|
|
{ duration: '2m', target: 0 },
|
|
],
|
|
};
|
|
|
|
export default function () {
|
|
let response = http.post('http://localhost:8000/api/auth/login', {
|
|
email: 'test@example.com',
|
|
password: 'password',
|
|
});
|
|
|
|
check(response, {
|
|
'status is 200': r => r.status === 200,
|
|
'response time < 500ms': r => r.timings.duration < 500,
|
|
});
|
|
}
|
|
```
|
|
|
|
## Notes
|
|
|
|
- **Test the behavior, not the implementation**
|
|
- **Keep tests simple and readable**
|
|
- **Use descriptive test names**
|
|
- **Mock external dependencies**
|
|
- **Run tests in CI/CD**
|
|
- **Monitor coverage, but don't chase 100%**
|
|
- **Review and refactor tests regularly**
|
|
|
|
## Related Skills
|
|
|
|
- [Debug Service](./debug-service.md) - When tests fail
|
|
- [Production Readiness](./production-readiness.md) - Tests part of readiness
|
|
- [Mobile Code Quality](./mobile-code-quality.md) - Mobile-specific testing
|