Skip to main content
This skill ensures all code development follows TDD principles with comprehensive test coverage.

When to Activate

  • Writing new features or functionality
  • Fixing bugs or issues
  • Refactoring existing code
  • Adding API endpoints
  • Creating new components

Core Principles

1. Tests BEFORE Code

ALWAYS write tests first, then implement code to make tests pass.

2. Coverage Requirements

  • Minimum 80% coverage (unit + integration + E2E)
  • All edge cases covered
  • Error scenarios tested
  • Boundary conditions verified

3. Test Types

Unit Tests

  • Individual functions and utilities
  • Component logic
  • Pure functions
  • Helpers and utilities

Integration Tests

  • API endpoints
  • Database operations
  • Service interactions
  • External API calls

E2E Tests (Playwright)

  • Critical user flows
  • Complete workflows
  • Browser automation
  • UI interactions

TDD Workflow Steps

1
Write User Journeys
2
As a [role], I want to [action], so that [benefit]

Example:
As a user, I want to search for markets semantically,
so that I can find relevant markets even without exact keywords.
3
Generate Test Cases
4
For each user journey, create comprehensive test cases:
5
describe('Semantic Search', () => {
  it('returns relevant markets for query', async () => {
    // Test implementation
  })

  it('handles empty query gracefully', async () => {
    // Test edge case
  })

  it('falls back to substring search when Redis unavailable', async () => {
    // Test fallback behavior
  })

  it('sorts results by similarity score', async () => {
    // Test sorting logic
  })
})
6
Run Tests (They Should Fail)
7
npm test
# Tests should fail - we haven't implemented yet
8
Implement Code
9
Write minimal code to make tests pass:
10
// Implementation guided by tests
export async function searchMarkets(query: string) {
  // Implementation here
}
11
Run Tests Again
12
npm test
# Tests should now pass
13
Refactor
14
Improve code quality while keeping tests green:
15
  • Remove duplication
  • Improve naming
  • Optimize performance
  • Enhance readability
  • 16
    Verify Coverage
    17
    npm run test:coverage
    # Verify 80%+ coverage achieved
    

    Testing Patterns

    Unit Test Pattern (Jest/Vitest)

    import { render, screen, fireEvent } from '@testing-library/react'
    import { Button } from './Button'
    
    describe('Button Component', () => {
      it('renders with correct text', () => {
        render(<Button>Click me</Button>)
        expect(screen.getByText('Click me')).toBeInTheDocument()
      })
    
      it('calls onClick when clicked', () => {
        const handleClick = jest.fn()
        render(<Button onClick={handleClick}>Click</Button>)
    
        fireEvent.click(screen.getByRole('button'))
    
        expect(handleClick).toHaveBeenCalledTimes(1)
      })
    
      it('is disabled when disabled prop is true', () => {
        render(<Button disabled>Click</Button>)
        expect(screen.getByRole('button')).toBeDisabled()
      })
    })
    

    API Integration Test Pattern

    import { NextRequest } from 'next/server'
    import { GET } from './route'
    
    describe('GET /api/markets', () => {
      it('returns markets successfully', async () => {
        const request = new NextRequest('http://localhost/api/markets')
        const response = await GET(request)
        const data = await response.json()
    
        expect(response.status).toBe(200)
        expect(data.success).toBe(true)
        expect(Array.isArray(data.data)).toBe(true)
      })
    
      it('validates query parameters', async () => {
        const request = new NextRequest('http://localhost/api/markets?limit=invalid')
        const response = await GET(request)
    
        expect(response.status).toBe(400)
      })
    
      it('handles database errors gracefully', async () => {
        // Mock database failure
        const request = new NextRequest('http://localhost/api/markets')
        // Test error handling
      })
    })
    

    E2E Test Pattern (Playwright)

    import { test, expect } from '@playwright/test'
    
    test('user can search and filter markets', async ({ page }) => {
      // Navigate to markets page
      await page.goto('/')
      await page.click('a[href="/markets"]')
    
      // Verify page loaded
      await expect(page.locator('h1')).toContainText('Markets')
    
      // Search for markets
      await page.fill('input[placeholder="Search markets"]', 'election')
    
      // Wait for debounce and results
      await page.waitForTimeout(600)
    
      // Verify search results displayed
      const results = page.locator('[data-testid="market-card"]')
      await expect(results).toHaveCount(5, { timeout: 5000 })
    
      // Verify results contain search term
      const firstResult = results.first()
      await expect(firstResult).toContainText('election', { ignoreCase: true })
    
      // Filter by status
      await page.click('button:has-text("Active")')
    
      // Verify filtered results
      await expect(results).toHaveCount(3)
    })
    

    Common Testing Mistakes to Avoid

    Wrong:
    // Don't test internal state
    expect(component.state.count).toBe(5)
    
    Correct:
    // Test what users see
    expect(screen.getByText('Count: 5')).toBeInTheDocument()
    
    Wrong:
    // Breaks easily
    await page.click('.css-class-xyz')
    
    Correct:
    // Resilient to changes
    await page.click('button:has-text("Submit")')
    await page.click('[data-testid="submit-button"]')
    
    Wrong:
    // Tests depend on each other
    test('creates user', () => { /* ... */ })
    test('updates same user', () => { /* depends on previous test */ })
    
    Correct:
    // Each test sets up its own data
    test('creates user', () => {
      const user = createTestUser()
      // Test logic
    })
    
    test('updates user', () => {
      const user = createTestUser()
      // Update logic
    })
    

    Best Practices

    1. Write Tests First - Always TDD
    2. One Assert Per Test - Focus on single behavior
    3. Descriptive Test Names - Explain what’s tested
    4. Arrange-Act-Assert - Clear test structure
    5. Mock External Dependencies - Isolate unit tests
    6. Test Edge Cases - Null, undefined, empty, large
    7. Test Error Paths - Not just happy paths
    8. Keep Tests Fast - Unit tests < 50ms each
    9. Clean Up After Tests - No side effects
    10. Review Coverage Reports - Identify gaps

    Success Metrics

    • 80%+ code coverage achieved
    • All tests passing (green)
    • No skipped or disabled tests
    • Fast test execution (< 30s for unit tests)
    • E2E tests cover critical user flows
    • Tests catch bugs before production
    Tests are not optional. They are the safety net that enables confident refactoring, rapid development, and production reliability.