
Testing Strategist
- 159 installs
- 33 repo stars
- Updated December 25, 2025
- daffy0208/ai-dev-standards
Design test pyramids, coverage matrices, and QA workflows for AI-assisted projects before release, spanning unit, integration, acceptance, and regression layers.
About
Testing strategist from daffy0208/ai-dev-standards helps teams craft structured QA plans for AI-assisted software, defining unit, integration, and acceptance coverage before code review and release.
- test pyramid planning
- coverage matrices
- AI dev standards
- integration test design
- acceptance criteria
Testing Strategist by the numbers
- 159 all-time installs (skills.sh)
- Ranked #861 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/daffy0208/ai-dev-standards --skill testing-strategistAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 159 |
|---|---|
| repo stars | ★ 33 |
| Last updated | December 25, 2025 |
| Repository | daffy0208/ai-dev-standards ↗ |
What it does
Design test pyramids, coverage matrices, and QA workflows for AI-assisted projects before release, spanning unit, integration, acceptance, and regression layers.
Files
Testing Strategist
Test the right things at the right level - write tests that give you confidence to ship.
Core Principle
The Testing Pyramid: 70% unit tests, 20% integration tests, 10% E2E tests.
Tests should be:
- Fast - Run in milliseconds (unit) to seconds (integration) to minutes (E2E)
- Isolated - Test one thing at a time
- Repeatable - Same input = same output
- Self-checking - Pass/fail automatically, no manual verification
- Timely - Written alongside code (or before, with TDD)
---
The Testing Pyramid
/\
/ \ E2E Tests (10%)
/----\ - Slow, brittle, expensive
/ \ - Test critical user journeys
/--------\ - Example: "User can complete checkout"
/ \
/------------\ Integration Tests (20%)
/ \ - Medium speed, test components together
/----------------\ - Example: "API endpoint returns correct data"
/ \
/--------------------\ Unit Tests (70%)
/______________________\ - Fast, isolated, test functions/components
- Example: "calculateTotal returns sum"Why This Ratio?
- Unit tests: Fast feedback, pinpoint bugs precisely, easy to maintain
- Integration tests: Ensure components work together, catch interface issues
- E2E tests: Verify actual user flows, catch UI bugs, but slow and brittle
---
Level 1: Unit Tests (70%)
What to Test
Test individual functions, components, or classes in isolation.
Good candidates:
- ✅ Business logic functions (calculations, validation, transformations)
- ✅ Utility functions (formatDate, parseUrl, etc.)
- ✅ React components (rendering, props, state)
- ✅ Hooks (custom React hooks)
- ✅ Pure functions (same input = same output)
Skip:
- ❌ Third-party libraries (assume they work)
- ❌ Framework internals (React, Next.js)
- ❌ Simple getters/setters with no logic
Unit Test Examples
Testing Business Logic (Jest + TypeScript)
// src/lib/pricing.ts
export function calculateTotal(items: { price: number; quantity: number }[]) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
export function applyDiscount(total: number, discountPercent: number) {
if (discountPercent < 0 || discountPercent > 100) {
throw new Error('Invalid discount percentage')
}
return total * (1 - discountPercent / 100)
}
// src/lib/pricing.test.ts
import { calculateTotal, applyDiscount } from './pricing'
describe('calculateTotal', () => {
it('calculates total for single item', () => {
const items = [{ price: 10, quantity: 2 }]
expect(calculateTotal(items)).toBe(20)
})
it('calculates total for multiple items', () => {
const items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 3 }
]
expect(calculateTotal(items)).toBe(35)
})
it('returns 0 for empty array', () => {
expect(calculateTotal([])).toBe(0)
})
})
describe('applyDiscount', () => {
it('applies discount correctly', () => {
expect(applyDiscount(100, 20)).toBe(80)
})
it('throws error for invalid discount', () => {
expect(() => applyDiscount(100, -10)).toThrow('Invalid discount')
expect(() => applyDiscount(100, 150)).toThrow('Invalid discount')
})
})Testing React Components (Jest + React Testing Library)
// src/components/Button.tsx
export function Button({
children,
variant = 'primary',
onClick
}: {
children: React.ReactNode
variant?: 'primary' | 'secondary'
onClick?: () => void
}) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{children}
</button>
)
}
// src/components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'
describe('Button', () => {
it('renders with correct text', () => {
render(<Button>Click me</Button>)
expect(screen.getByText('Click me')).toBeInTheDocument()
})
it('applies primary variant by default', () => {
render(<Button>Click me</Button>)
expect(screen.getByRole('button')).toHaveClass('btn-primary')
})
it('applies secondary variant when specified', () => {
render(<Button variant="secondary">Click me</Button>)
expect(screen.getByRole('button')).toHaveClass('btn-secondary')
})
it('calls onClick when clicked', () => {
const handleClick = jest.fn()
render(<Button onClick={handleClick}>Click me</Button>)
fireEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
})Testing Custom Hooks
// src/hooks/useCounter.ts
import { useState } from 'react'
export function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue)
const increment = () => setCount(c => c + 1)
const decrement = () => setCount(c => c - 1)
const reset = () => setCount(initialValue)
return { count, increment, decrement, reset }
}
// src/hooks/useCounter.test.ts
import { renderHook, act } from '@testing-library/react'
import { useCounter } from './useCounter'
describe('useCounter', () => {
it('initializes with default value', () => {
const { result } = renderHook(() => useCounter())
expect(result.current.count).toBe(0)
})
it('initializes with custom value', () => {
const { result } = renderHook(() => useCounter(10))
expect(result.current.count).toBe(10)
})
it('increments count', () => {
const { result } = renderHook(() => useCounter())
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})
it('decrements count', () => {
const { result } = renderHook(() => useCounter(5))
act(() => {
result.current.decrement()
})
expect(result.current.count).toBe(4)
})
it('resets to initial value', () => {
const { result } = renderHook(() => useCounter(10))
act(() => {
result.current.increment()
result.current.increment()
result.current.reset()
})
expect(result.current.count).toBe(10)
})
})Unit Test Best Practices
✅ Do:
- Test behavior, not implementation
- Use descriptive test names (it should...)
- Follow AAA pattern: Arrange, Act, Assert
- Test edge cases (empty arrays, null, negative numbers)
- Keep tests simple and readable
❌ Don't:
- Test private methods directly
- Over-mock (makes tests brittle)
- Test framework internals
- Write tests that depend on other tests
---
Level 2: Integration Tests (20%)
What to Test
Test multiple units working together - typically API routes, database operations, or service integrations.
Good candidates:
- ✅ API endpoints (request → controller → database → response)
- ✅ Database operations (queries, transactions)
- ✅ Third-party integrations (Stripe, SendGrid)
- ✅ Authentication flows
- ✅ File upload/download
Integration Test Examples
Testing API Routes (Next.js + Supertest)
// app/api/posts/route.ts
export async function GET() {
const posts = await db.post.findMany({
include: { author: true },
orderBy: { createdAt: 'desc' }
})
return Response.json(posts)
}
export async function POST(request: Request) {
const body = await request.json()
// Validate
const result = PostSchema.safeParse(body)
if (!result.success) {
return Response.json({ errors: result.error.issues }, { status: 400 })
}
// Create post
const post = await db.post.create({
data: {
title: result.data.title,
content: result.data.content,
authorId: request.user.id
}
})
return Response.json(post, { status: 201 })
}
// app/api/posts/route.test.ts
import { testClient } from '@/lib/test-utils'
describe('POST /api/posts', () => {
beforeEach(async () => {
// Clean database before each test
await db.post.deleteMany()
})
it('creates a new post', async () => {
const response = await testClient
.post('/api/posts')
.set('Authorization', `Bearer ${authToken}`)
.send({
title: 'Test Post',
content: 'This is a test post'
})
expect(response.status).toBe(201)
expect(response.body).toMatchObject({
title: 'Test Post',
content: 'This is a test post'
})
// Verify in database
const posts = await db.post.findMany()
expect(posts).toHaveLength(1)
expect(posts[0].title).toBe('Test Post')
})
it('returns 400 for invalid data', async () => {
const response = await testClient
.post('/api/posts')
.set('Authorization', `Bearer ${authToken}`)
.send({
title: '' // Invalid: empty title
})
expect(response.status).toBe(400)
expect(response.body.errors).toBeDefined()
})
it('returns 401 for unauthenticated request', async () => {
const response = await testClient.post('/api/posts').send({
title: 'Test',
content: 'Test'
})
expect(response.status).toBe(401)
})
})
describe('GET /api/posts', () => {
beforeEach(async () => {
await db.post.deleteMany()
// Seed test data
await db.post.createMany({
data: [
{ title: 'Post 1', content: 'Content 1', authorId: user.id },
{ title: 'Post 2', content: 'Content 2', authorId: user.id }
]
})
})
it('returns all posts', async () => {
const response = await testClient.get('/api/posts')
expect(response.status).toBe(200)
expect(response.body).toHaveLength(2)
expect(response.body[0].author).toBeDefined()
})
})Testing Database Operations
// src/lib/repositories/userRepository.test.ts
import { db } from '@/lib/db'
import { createUser, findUserByEmail, updateUser } from './userRepository'
describe('userRepository', () => {
beforeEach(async () => {
await db.user.deleteMany()
})
afterAll(async () => {
await db.$disconnect()
})
describe('createUser', () => {
it('creates user with hashed password', async () => {
const user = await createUser({
email: 'test@example.com',
password: 'password123'
})
expect(user.email).toBe('test@example.com')
expect(user.password).not.toBe('password123') // Should be hashed
expect(user.password).toMatch(/^\$2[aby]/) // bcrypt hash format
})
it('throws error for duplicate email', async () => {
await createUser({ email: 'test@example.com', password: 'pass' })
await expect(createUser({ email: 'test@example.com', password: 'pass' })).rejects.toThrow()
})
})
describe('findUserByEmail', () => {
it('finds existing user', async () => {
await createUser({ email: 'test@example.com', password: 'pass' })
const user = await findUserByEmail('test@example.com')
expect(user).toBeDefined()
expect(user?.email).toBe('test@example.com')
})
it('returns null for non-existent user', async () => {
const user = await findUserByEmail('nonexistent@example.com')
expect(user).toBeNull()
})
})
})Integration Test Best Practices
✅ Do:
- Use test database (separate from development/production)
- Clean up test data (beforeEach/afterEach)
- Test happy path + error cases
- Test authentication/authorization
- Use factories/fixtures for test data
❌ Don't:
- Test against production database
- Leave test data behind
- Mock database (defeats purpose of integration test)
- Depend on external services (mock external APIs)
---
Level 3: E2E Tests (10%)
What to Test
Test complete user journeys through the actual UI.
Good candidates:
- ✅ Critical user flows (signup, login, checkout)
- ✅ Core business processes
- ✅ Multi-step workflows
Skip:
- ❌ Every possible UI interaction (too slow/brittle)
- ❌ Edge cases (cover with unit/integration tests)
E2E Test Examples (Playwright)
// tests/e2e/auth.spec.ts
import { test, expect } from '@playwright/test'
test.describe('Authentication', () => {
test('user can sign up and log in', async ({ page }) => {
// Navigate to signup
await page.goto('/signup')
// Fill signup form
await page.fill('input[name="email"]', 'test@example.com')
await page.fill('input[name="password"]', 'SecurePass123!')
await page.fill('input[name="confirmPassword"]', 'SecurePass123!')
// Submit form
await page.click('button[type="submit"]')
// Should redirect to dashboard
await expect(page).toHaveURL('/dashboard')
await expect(page.locator('h1')).toContainText('Welcome')
// Logout
await page.click('[data-testid="logout-button"]')
// Should redirect to login
await expect(page).toHaveURL('/login')
// Login again
await page.fill('input[name="email"]', 'test@example.com')
await page.fill('input[name="password"]', 'SecurePass123!')
await page.click('button[type="submit"]')
// Should be back at dashboard
await expect(page).toHaveURL('/dashboard')
})
test('shows error for invalid credentials', async ({ page }) => {
await page.goto('/login')
await page.fill('input[name="email"]', 'wrong@example.com')
await page.fill('input[name="password"]', 'wrongpassword')
await page.click('button[type="submit"]')
// Should show error message
await expect(page.locator('[role="alert"]')).toContainText('Invalid credentials')
// Should stay on login page
await expect(page).toHaveURL('/login')
})
})
// tests/e2e/checkout.spec.ts
test.describe('Checkout Flow', () => {
test('user can complete purchase', async ({ page }) => {
// Login first
await page.goto('/login')
await page.fill('input[name="email"]', 'test@example.com')
await page.fill('input[name="password"]', 'password')
await page.click('button[type="submit"]')
// Add product to cart
await page.goto('/products')
await page.click('[data-testid="product-1"] button:text("Add to Cart")')
// Verify cart badge
await expect(page.locator('[data-testid="cart-badge"]')).toContainText('1')
// Go to checkout
await page.click('[data-testid="cart-button"]')
await page.click('button:text("Checkout")')
// Fill shipping info
await page.fill('input[name="address"]', '123 Main St')
await page.fill('input[name="city"]', 'San Francisco')
await page.fill('input[name="zip"]', '94103')
// Fill payment info (test mode)
await page.fill('input[name="cardNumber"]', '4242424242424242')
await page.fill('input[name="expiry"]', '12/25')
await page.fill('input[name="cvc"]', '123')
// Submit order
await page.click('button:text("Place Order")')
// Should see confirmation
await expect(page).toHaveURL(/\/orders\/\d+/)
await expect(page.locator('h1')).toContainText('Order Confirmed')
})
})E2E Test Best Practices
✅ Do:
- Test critical paths only (< 20 tests)
- Use data-testid attributes (stable selectors)
- Run in CI/CD pipeline
- Test across browsers (Chrome, Firefox, Safari)
- Take screenshots on failure
❌ Don't:
- Test every UI variation
- Use fragile selectors (text content, nth-child)
- Run E2E tests on every commit (too slow)
- Ignore flaky tests (fix or remove them)
---
Test-Driven Development (TDD)
The Red-Green-Refactor Cycle
1. Red: Write a failing test 2. Green: Write minimal code to make it pass 3. Refactor: Improve code while keeping tests green
TDD Example
// 1. RED: Write failing test first
describe('formatCurrency', () => {
it('formats number as USD currency', () => {
expect(formatCurrency(1234.56)).toBe('$1,234.56')
})
})
// Run test: FAILS (formatCurrency doesn't exist)
// 2. GREEN: Write minimal implementation
export function formatCurrency(amount: number): string {
return `$${amount.toLocaleString('en-US', { minimumFractionDigits: 2 })}`
}
// Run test: PASSES
// 3. REFACTOR: Improve implementation
export function formatCurrency(
amount: number,
currency: string = 'USD',
locale: string = 'en-US'
): string {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency
}).format(amount)
}
// Add more tests
it('formats EUR currency', () => {
expect(formatCurrency(1234.56, 'EUR', 'de-DE')).toBe('1.234,56 €')
})
it('handles negative amounts', () => {
expect(formatCurrency(-100)).toBe('-$100.00')
})When to Use TDD
Good for:
- ✅ Complex business logic
- ✅ Bug fixes (write test that reproduces bug first)
- ✅ Well-defined requirements
- ✅ Critical algorithms
Skip for:
- ❌ Exploratory coding (don't know requirements yet)
- ❌ Throwaway prototypes
- ❌ Simple CRUD operations
---
Mocking Strategies
When to Mock
- ✅ External APIs (slow, unreliable, cost money)
- ✅ Time/randomness (make tests deterministic)
- ✅ File system operations
- ✅ Database (in unit tests only)
Mock Examples (Jest)
// Mock external API
import { fetchUserData } from '@/lib/api'
jest.mock('@/lib/api')
const mockFetchUserData = fetchUserData as jest.MockedFunction<typeof fetchUserData>
it('displays user data', async () => {
mockFetchUserData.mockResolvedValue({
id: '1',
name: 'John Doe',
email: 'john@example.com'
})
render(<UserProfile userId="1" />)
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument()
})
})
// Mock Date
beforeAll(() => {
jest.useFakeTimers()
jest.setSystemTime(new Date('2024-01-01'))
})
afterAll(() => {
jest.useRealTimers()
})
it('shows correct date', () => {
expect(getCurrentDate()).toBe('2024-01-01')
})
// Mock Math.random
const mockRandom = jest.spyOn(Math, 'random')
mockRandom.mockReturnValue(0.5)
expect(generateRandomId()).toBe('expected-id-with-0.5-random')
mockRandom.mockRestore()Mocking Best Practices
✅ Do:
- Mock at boundaries (APIs, file system)
- Restore mocks after tests
- Make mocks realistic (same shape as real data)
❌ Don't:
- Over-mock (makes tests brittle)
- Mock your own code (test real behavior)
- Mock what you don't own (unless external)
---
Code Coverage
Coverage Targets
- 70% minimum - Below this, you're missing important tests
- 80% good - Solid coverage of critical paths
- 90%+ diminishing returns - Chasing 100% often not worth it
What to Focus On
High priority (must have 90%+ coverage):
- Business logic
- Authentication/authorization
- Payment processing
- Data validation
Medium priority (aim for 70%+):
- API routes
- Database queries
- Utility functions
Low priority (okay to skip):
- UI components (test behavior, not implementation)
- Configuration files
- Type definitions
- Third-party integrations (integration tests better)
Checking Coverage
# Jest
npm test -- --coverage
# View HTML report
open coverage/lcov-report/index.htmlCoverage Configuration (jest.config.js)
module.exports = {
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/*.stories.tsx',
'!src/types/**'
],
coverageThresholds: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70
},
// Critical paths need higher coverage
'./src/lib/auth/**': {
branches: 90,
functions: 90,
lines: 90
}
}
}---
Testing Strategies by Framework
Next.js (React)
- Unit: Jest + React Testing Library
- Integration: Supertest (API routes)
- E2E: Playwright
Express API
- Unit: Jest
- Integration: Supertest
- E2E: Playwright (if has UI)
FastAPI (Python)
- Unit: pytest
- Integration: pytest + TestClient
- E2E: Playwright
---
Common Testing Patterns
Testing Async Code
// Using async/await
it('fetches user data', async () => {
const user = await fetchUser('123')
expect(user.name).toBe('John')
})
// Using waitFor (React Testing Library)
it('shows loading then data', async () => {
render(<UserProfile userId="123" />)
expect(screen.getByText('Loading...')).toBeInTheDocument()
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument()
})
})Testing Error Handling
it('handles errors gracefully', async () => {
mockFetchUser.mockRejectedValue(new Error('Network error'))
render(<UserProfile userId="123" />)
await waitFor(() => {
expect(screen.getByText(/error/i)).toBeInTheDocument()
})
})Testing Forms
it('submits form with valid data', async () => {
const handleSubmit = jest.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: 'Login' }))
await waitFor(() => {
expect(handleSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123'
})
})
})---
Test Organization
File Structure
src/
├── components/
│ ├── Button.tsx
│ └── Button.test.tsx # Co-located with component
├── lib/
│ ├── utils.ts
│ └── utils.test.ts # Co-located with module
└── __tests__/
├── integration/ # Integration tests
│ └── api.test.ts
└── e2e/ # E2E tests
└── checkout.spec.tsNaming Conventions
- Unit/Integration:
*.test.tsor*.spec.ts - E2E:
*.e2e.tsor*.spec.ts(in tests/e2e/) - Test names:
it('should do X when Y')orit('does X')
---
When to Use This Skill
Use testing-strategist skill when:
- ✅ Setting up testing for new project
- ✅ Choosing test frameworks
- ✅ Deciding what to test and at what level
- ✅ Implementing TDD
- ✅ Improving code coverage
- ✅ Fixing flaky tests
---
Related Resources
Skills:
security-engineer- Security testingapi-designer- API testing strategiesfrontend-builder- React testing patterns
Patterns:
/STANDARDS/best-practices/testing-best-practices.md/TEMPLATES/testing/jest-nextjs-setup.md/TEMPLATES/testing/playwright-e2e-setup.md
External:
---
Good tests give you confidence to ship. ✅
name: testing-strategist
kind: skill
description: Design and implement comprehensive testing strategies with unit tests, integration tests, E2E tests, and test-driven development
preconditions:
- check: file_exists('package.json') or file_exists('requirements.txt')
description: Project initialized with package manager
required: true
- check: has_source_code_to_test()
description: Application code exists to test
required: true
- check: not file_exists('jest.config.js') and not file_exists('vitest.config.ts')
description: Testing framework not already configured
required: false
effects:
- configures_testing_framework
- creates_unit_tests
- creates_integration_tests
- creates_e2e_tests
- implements_test_coverage
- adds_ci_test_pipeline
- creates_test_fixtures
- implements_mocking_strategy
- adds_test_documentation
domains:
- testing
- quality-assurance
- tdd
- unit-testing
- integration-testing
- e2e-testing
- ci-cd
cost: low
latency: fast
risk_level: safe
side_effects:
- modifies_files
- installs_test_dependencies
- creates_test_files
- configures_ci_pipeline
idempotent: false
success_signal: "Tests run successfully, coverage meets targets (70%+), CI pipeline passes, test suite is maintainable"
failure_signals:
- "Tests fail to run"
- "Coverage below targets"
- "Flaky tests"
- "Tests too slow"
- "Mocking issues"
compatibility:
requires:
- testing-framework
- source-code
conflicts_with:
- incompatible-test-framework
composes_with:
- frontend-builder
- api-designer
- security-engineer
- performance-optimizer
- ci-cd-pipeline
enables:
- continuous-integration
- test-driven-development
- regression-prevention
- code-quality-monitoring
observability:
logs:
- "Configuring {testing_framework} for {project_type}"
- "Creating test suite for {module_name}"
- "Running {test_count} tests..."
- "Coverage: {coverage_percent}% ({lines_covered}/{lines_total} lines)"
metrics:
- test_count
- test_pass_rate
- test_execution_time_ms
- code_coverage_percent
- flaky_test_count
metadata:
version: "1.0.0"
created_at: "2025-10-28"
tags:
- testing
- jest
- playwright
- tdd
- quality
- ci-cd
- coverage
examples:
- "Set up Jest for React testing"
- "Implement TDD workflow"
- "Create E2E tests with Playwright"
- "Configure coverage reporting in CI"
Testing Strategist - Quick Start
Version: 1.0.0 Category: Technical Development Difficulty: Intermediate
What This Skill Does
Helps you design and implement comprehensive testing strategies using the testing pyramid (70% unit, 20% integration, 10% E2E).
When to Use
Use this skill when you need to:
- Set up testing for a new project
- Choose testing frameworks
- Decide what to test at which level
- Implement test-driven development (TDD)
- Improve code coverage
- Write unit, integration, or E2E tests
- Fix flaky tests
Quick Start
Fastest path to testing setup:
1. Choose frameworks:
- Unit/Integration: Jest (JavaScript/TypeScript) or pytest (Python)
- React: React Testing Library
- E2E: Playwright
2. Follow the pyramid: 70% unit, 20% integration, 10% E2E 3. Coverage target: 70% minimum, 80% goal 4. Write tests for:
- Business logic (unit)
- API endpoints (integration)
- Critical user flows (E2E)
Time to setup: 1-2 hours
File Structure
testing-strategist/
├── SKILL.md # Main skill instructions (start here)
└── README.md # This filePrerequisites
Knowledge:
- Basic programming
- Understanding of your framework (React, Express, etc.)
Tools:
- Jest, Vitest, or pytest
- React Testing Library (for React)
- Playwright or Cypress (for E2E)
Related Skills:
frontend-builder- React component testingapi-designer- API testing patternssecurity-engineer- Security testing
Success Criteria
You've successfully used this skill when:
- ✅ Testing pyramid established (70/20/10)
- ✅ Test frameworks configured
- ✅ Code coverage ≥ 70%
- ✅ Critical paths have tests
- ✅ Tests run in CI/CD
- ✅ Tests are fast and reliable
Common Workflows
Workflow 1: New Project Testing Setup
1. Install Jest + React Testing Library 2. Configure jest.config.js 3. Write first unit test 4. Add integration tests for APIs 5. Add Playwright for E2E 6. Configure CI/CD to run tests
Workflow 2: Test-Driven Development
1. Write failing test (red) 2. Write minimal code to pass (green) 3. Refactor while keeping tests green 4. Repeat
Workflow 3: Improve Coverage
1. Run npm test -- --coverage 2. Identify untested files 3. Add unit tests for business logic 4. Add integration tests for APIs 5. Target 70% coverage minimum
Key Concepts
Testing Pyramid:
- Unit Tests (70%): Fast, isolated, test functions/components
- Integration Tests (20%): Test components together, APIs + DB
- E2E Tests (10%): Slow, test complete user flows
What to Test:
- ✅ Business logic, utilities, components
- ✅ API endpoints, database operations
- ✅ Critical user journeys
- ❌ Third-party libraries, framework internals
Coverage Targets:
- 70%: Minimum acceptable
- 80%: Good coverage
- 90%+: Diminishing returns
Troubleshooting
Skill not activating?
- Try explicitly requesting: "Use the testing-strategist skill to..."
- Mention keywords: "testing", "unit tests", "E2E"
Tests are slow?
- Too many E2E tests (shift left to integration/unit)
- Not using parallel execution
- Inefficient setup/teardown
Tests are flaky?
- Using fragile selectors (E2E)
- Race conditions (add proper waits)
- Shared state between tests
- External dependencies not mocked
Low coverage?
- Not testing business logic
- Missing edge cases
- Too much code in components (extract to functions)
Quick Reference
Jest Configuration
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom', // for React
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
coverageThresholds: {
global: { lines: 70 }
}
}Common Commands
# Run all tests
npm test
# Watch mode
npm test -- --watch
# Coverage report
npm test -- --coverage
# Run specific test
npm test Button.test.ts
# E2E tests
npx playwright testTest Anatomy (AAA Pattern)
it('should do something', () => {
// Arrange - Set up test data
const input = { x: 1, y: 2 }
// Act - Execute the function
const result = add(input.x, input.y)
// Assert - Verify the outcome
expect(result).toBe(3)
})Version History
- 1.0.0 (2025-10-22): Initial release, testing pyramid, TDD
License
Part of ai-dev-standards repository.