# 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
