learning_ai_common_plat/AI.dev/SKILLS/test-strategies.md
saravanakumardb1 c3b869ceb9 feat: create AI.dev/SKILLS repository with reusable development skills
- 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
2026-02-12 17:13:16 -08:00

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