testing-strategy

Testing Strategy Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "testing-strategy" with this command: npx skills add atman36/ai-vibe-prompts/atman36-ai-vibe-prompts-testing-strategy

Testing Strategy Skill

Objective

Implement comprehensive testing strategy covering unit, integration, and E2E tests using modern tools (Vitest, Playwright) with clear coverage targets and best practices.

When to Use This Skill

Auto-invoke when:

  • User mentions "test", "testing", "coverage", "TDD", "E2E"

  • Setting up new project

  • Adding new features (need tests)

  • Debugging test failures

  • Improving test coverage

Testing Pyramid

    /\
   /E2E\         Few, slow, expensive
  /------\
 /  Integ \      Some, moderate speed
/----------\

/ Unit Tests \ Many, fast, cheap /--------------\

Distribution:

  • 70% Unit Tests - Fast, isolated, cheap

  • 20% Integration Tests - Moderate speed, test interactions

  • 10% E2E Tests - Slow, expensive, critical user flows

Test Types

  1. Unit Tests (Vitest)

What: Test individual functions/components in isolation

Tools: Vitest, React Testing Library

Coverage Target: 80%+

Setup:

npm install -D vitest @vitest/ui @testing-library/react @testing-library/jest-dom

Config (vitest.config.ts ):

import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react'

export default defineConfig({ plugins: [react()], test: { globals: true, environment: 'jsdom', setupFiles: './tests/setup.ts', coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], exclude: ['node_modules/', 'tests/'], thresholds: { lines: 80, functions: 80, branches: 75, statements: 80 } } } })

Example (Button.test.tsx ):

import { render, screen, fireEvent } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' import { Button } from './Button'

describe('Button', () => { it('renders with text', () => { render(<Button>Click me</Button>) expect(screen.getByText('Click me')).toBeInTheDocument() })

it('calls onClick when clicked', () => { const handleClick = vi.fn() render(<Button onClick={handleClick}>Click</Button>) fireEvent.click(screen.getByText('Click')) expect(handleClick).toHaveBeenCalledOnce() })

it('is disabled when disabled prop is true', () => { render(<Button disabled>Disabled</Button>) expect(screen.getByRole('button')).toBeDisabled() }) })

Commands:

npm run test # Run all tests npm run test:watch # Watch mode npm run test:ui # Visual UI npm run test:coverage # With coverage

  1. Integration Tests (Vitest)

What: Test component interactions, API calls, state management

Example (UserProfile.test.tsx ):

import { render, screen, waitFor } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' import { UserProfile } from './UserProfile'

// Mock API vi.mock('./api', () => ({ fetchUser: vi.fn(() => Promise.resolve({ id: 1, name: 'John Doe', email: 'john@example.com' })) }))

describe('UserProfile Integration', () => { it('fetches and displays user data', async () => { render(<UserProfile userId="1" />)

expect(screen.getByText('Loading...')).toBeInTheDocument()

await waitFor(() => {
  expect(screen.getByText('John Doe')).toBeInTheDocument()
  expect(screen.getByText('john@example.com')).toBeInTheDocument()
})

}) })

  1. E2E Tests (Playwright)

What: Test complete user flows in real browser

Tools: Playwright

Coverage Target: Critical paths only

Setup:

npm install -D @playwright/test npx playwright install

Config (playwright.config.ts ):

import { defineConfig, devices } from '@playwright/test'

export default defineConfig({ testDir: './e2e', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: 'html', use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'mobile', use: { ...devices['iPhone 13'] }, }, ], webServer: { command: 'npm run dev', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, })

Example (e2e/auth.spec.ts ):

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

test.describe('Authentication Flow', () => { test('user can sign up and log in', async ({ page }) => { // Sign up await page.goto('/signup') await page.fill('[name="email"]', 'test@example.com') await page.fill('[name="password"]', 'SecurePass123!') await page.click('button[type="submit"]')

// Should redirect to dashboard
await expect(page).toHaveURL(/\/dashboard/)
await expect(page.locator('h1')).toContainText('Welcome')

// Log out
await page.click('[aria-label="User menu"]')
await page.click('text=Logout')

// Should redirect to home
await expect(page).toHaveURL('/')

// Log back in
await page.goto('/login')
await page.fill('[name="email"]', 'test@example.com')
await page.fill('[name="password"]', 'SecurePass123!')
await page.click('button[type="submit"]')

await expect(page).toHaveURL(/\/dashboard/)

}) })

Commands:

npx playwright test # Run all E2E npx playwright test --ui # Interactive mode npx playwright test --headed # Show browser npx playwright test --project=chromium # Specific browser npx playwright show-report # View last report

Testing Best Practices

AAA Pattern

// Arrange const user = { id: 1, name: 'John' } const mockFetch = vi.fn()

// Act const result = await fetchUser(mockFetch, 1)

// Assert expect(result).toEqual(user) expect(mockFetch).toHaveBeenCalledWith('/api/users/1')

Test Naming

// Good: descriptive, explains what and when it('displays error message when API returns 404', () => {}) it('disables submit button when form is invalid', () => {})

// Bad: vague, unclear it('works', () => {}) it('test 1', () => {})

One Assertion Per Test (Guideline)

// Prefer focused tests it('renders user name', () => { render(<User name="John" />) expect(screen.getByText('John')).toBeInTheDocument() })

it('renders user email', () => { render(<User email="john@example.com" />) expect(screen.getByText('john@example.com')).toBeInTheDocument() })

// Over complex tests it('renders user data', () => { // Multiple unrelated assertions })

Mock External Dependencies

// Mock API calls vi.mock('./api', () => ({ fetchUser: vi.fn() }))

// Mock environment vi.stubEnv('API_URL', 'http://test-api.com')

// Mock timers vi.useFakeTimers() const now = new Date('2024-01-01') vi.setSystemTime(now)

Coverage Strategy

What to Test

✅ Do Test:

  • Business logic

  • Edge cases and error handling

  • User interactions

  • API integration

  • State management

  • Validation logic

  • Critical user flows (E2E)

❌ Don't Test:

  • Third-party libraries

  • Framework internals

  • Constants

  • Simple getters/setters

  • Generated code

Coverage Targets

Minimum:

  • Lines: 80%

  • Functions: 80%

  • Branches: 75%

  • Statements: 80%

Ideal:

  • Critical paths: 100%

  • Business logic: 95%+

  • UI components: 85%+

  • Utilities: 90%+

Run Coverage

npm run test:coverage

View in browser

open coverage/index.html

Testing Workflow

  1. TDD Approach (Recommended)

  2. Write failing test

  3. Write minimal code to pass

  4. Refactor

  5. Repeat

  6. Test-After (Pragmatic)

  7. Implement feature

  8. Write tests

  9. Achieve 80%+ coverage

  10. Refactor with confidence

  11. Pre-Commit Testing

Run before every commit

npm run test:quick # Fast unit tests npm run lint npm run typecheck

Run before push

npm run test # All unit/integration npm run test:coverage # Verify coverage

Run before deploy

npm run test:e2e # Full E2E suite

Test Organization

Directory Structure

src/ ├── components/ │ ├── Button/ │ │ ├── Button.tsx │ │ ├── Button.test.tsx # Co-located │ │ └── Button.stories.tsx # Storybook │ └── ... tests/ ├── setup.ts # Test setup ├── utils/ # Test utilities │ ├── renderWithProviders.tsx # Custom render │ └── mockData.ts # Test fixtures └── mocks/ # Global mocks e2e/ ├── auth.spec.ts ├── checkout.spec.ts └── fixtures/ # E2E test data

Naming Conventions

  • Unit/Integration: *.test.ts or *.test.tsx

  • E2E: *.spec.ts

  • Setup: setup.ts , vitest.config.ts

Continuous Integration

GitHub Actions Example

name: Tests on: [push, pull_request]

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 cache: 'npm'

  - run: npm ci
  - run: npm run lint
  - run: npm run typecheck
  - run: npm run test:coverage
  
  - name: Upload coverage
    uses: codecov/codecov-action@v3
    with:
      files: ./coverage/coverage-final.json

e2e: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 - run: npm ci - run: npx playwright install --with-deps - run: npm run build - run: npx playwright test

  - uses: actions/upload-artifact@v3
    if: always()
    with:
      name: playwright-report
      path: playwright-report/

Debugging Tests

Vitest

Run single test file

npm run test -- Button.test.tsx

Run tests matching pattern

npm run test -- --grep "Button renders"

Debug in VS Code

Add breakpoint, press F5

Playwright

Debug mode

npx playwright test --debug

Specific test

npx playwright test auth.spec.ts --debug

Trace viewer

npx playwright show-trace trace.zip

Common Testing Patterns

Testing Async Code

it('fetches user data', async () => { const { result } = renderHook(() => useUser(1))

await waitFor(() => { expect(result.current.data).toEqual({ id: 1, name: 'John' }) }) })

Testing Error States

it('displays error when fetch fails', async () => { vi.mocked(fetchUser).mockRejectedValue(new Error('Network error'))

render(<UserProfile userId="1" />)

await waitFor(() => { expect(screen.getByText(/error/i)).toBeInTheDocument() }) })

Testing Forms

it('submits form with valid data', async () => { const handleSubmit = vi.fn() render(<LoginForm onSubmit={handleSubmit} />)

await userEvent.type(screen.getByLabelText('Email'), 'test@example.com') await userEvent.type(screen.getByLabelText('Password'), 'password123') await userEvent.click(screen.getByRole('button', { name: /submit/i }))

expect(handleSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: 'password123' }) })

Integration with Other Skills

  • quality-gates

  • Run tests as quality check

  • git-workflow

  • Tests in pre-commit hooks

  • codebase-analysis

  • Identify untested code

Package.json Scripts

{ "scripts": { "test": "vitest", "test:watch": "vitest --watch", "test:ui": "vitest --ui", "test:coverage": "vitest --coverage", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", "test:e2e:headed": "playwright test --headed", "test:all": "npm run test:coverage && npm run test:e2e" } }

Version History

  • 1.0.0 (2025-01-03): Initial testing strategy with Vitest and Playwright

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

nextjs-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
General

quality-gates

No summary provided by upstream source.

Repository SourceNeeds Review
General

frontend-design

No summary provided by upstream source.

Repository SourceNeeds Review