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

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