- 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
11 KiB
11 KiB
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)
# 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
# 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)
# 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
// 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
# 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
// 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)
# 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
// 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
// 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)
# 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)
// 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
# Good: descriptive and clear
def test_user_login_with_valid_credentials_returns_token()
# Bad: vague
def test_login()
2. AAA Pattern (Arrange, Act, Assert)
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
# 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
// 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
# Python
coverage html
open htmlcov/index.html
# TypeScript
open coverage/lcov-report/index.html
CI/CD Integration
GitHub Actions
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
# 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
# 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)
// 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 - When tests fail
- Production Readiness - Tests part of readiness
- Mobile Code Quality - Mobile-specific testing