Files
gh-bandofai-puerto-plugins-…/skills/testing-practices/SKILL.md
2025-11-29 17:59:49 +08:00

3.8 KiB

Testing Practices

Comprehensive testing strategies across the stack

Testing Pyramid

Unit Tests (70%)

  • Test individual functions/methods
  • Fast execution
  • No external dependencies
  • High coverage of business logic

Integration Tests (20%)

  • Test component interactions
  • Database queries
  • API endpoints
  • Service integrations

E2E Tests (10%)

  • Test user workflows
  • Full stack testing
  • Browser automation
  • Critical paths only

Frontend Testing

Component Testing (React)

import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'

describe('Button', () => {
  it('calls onClick when clicked', () => {
    const onClick = jest.fn()
    render(<Button onClick={onClick}>Click me</Button>)

    fireEvent.click(screen.getByText('Click me'))

    expect(onClick).toHaveBeenCalledTimes(1)
  })
})

State Management Testing

import { renderHook, act } from '@testing-library/react-hooks'
import { useCounter } from './useCounter'

it('increments counter', () => {
  const { result } = renderHook(() => useCounter())

  act(() => {
    result.current.increment()
  })

  expect(result.current.count).toBe(1)
})

E2E Testing (Playwright)

import { test, expect } from '@playwright/test'

test('user can log in', async ({ page }) => {
  await page.goto('/login')
  await page.fill('[name=email]', 'user@example.com')
  await page.fill('[name=password]', 'password123')
  await page.click('button[type=submit]')

  await expect(page).toHaveURL('/dashboard')
  await expect(page.locator('h1')).toContainText('Welcome')
})

Backend Testing

API Testing

import request from 'supertest'
import { app } from './app'

describe('POST /api/users', () => {
  it('creates a new user', async () => {
    const res = await request(app)
      .post('/api/users')
      .send({
        name: 'John Doe',
        email: 'john@example.com'
      })
      .expect(201)

    expect(res.body).toHaveProperty('id')
    expect(res.body.email).toBe('john@example.com')
  })
})

Database Testing

beforeEach(async () => {
  await db.migrate.rollback()
  await db.migrate.latest()
  await db.seed.run()
})

afterEach(async () => {
  await db.destroy()
})

Test Best Practices

AAA Pattern

it('example test', () => {
  // Arrange
  const input = { ... }

  // Act
  const result = functionUnderTest(input)

  // Assert
  expect(result).toBe(expected)
})

Test Isolation

  • Each test should be independent
  • No shared state between tests
  • Clean up after each test
  • Use beforeEach/afterEach appropriately

Meaningful Test Names

// ❌ BAD
it('works', () => { ... })

// ✅ GOOD
it('returns 404 when user not found', () => { ... })

Mocking

jest.mock('./api')

it('handles API errors', async () => {
  api.fetchUser.mockRejectedValue(new Error('Network error'))

  // Test error handling
})

Performance Testing

Load Testing

  • Apache JMeter
  • k6
  • Artillery

Stress Testing

  • Identify breaking points
  • Test recovery
  • Monitor resource usage

Test Coverage

Coverage Metrics

  • Line coverage: 80%+ target
  • Branch coverage: 75%+ target
  • Function coverage: 90%+ target

Coverage Tools

  • Jest (--coverage)
  • Istanbul/nyc
  • Codecov/Coveralls

CI/CD Integration

GitHub Actions

name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: npm install
      - run: npm test
      - run: npm run test:e2e

Test Stages

  1. Lint and type check
  2. Unit tests
  3. Integration tests
  4. Build verification
  5. E2E tests (on staging)

Debugging Tests

  • Use test.only to isolate
  • Add console.logs strategically
  • Use debugger statements
  • Check test environment setup
  • Verify mock implementations