testing-strategy
17
总安装量
7
周安装量
#20445
全站排名
安装命令
npx skills add https://github.com/srstomp/pokayokay --skill testing-strategy
Agent 安装分布
claude-code
5
codex
4
antigravity
4
opencode
4
gemini-cli
4
windsurf
3
Skill 文档
Testing Strategy
Comprehensive testing guidance for test architecture, coverage strategy, and test design.
Test Pyramid
â²
â± â² E2E Tests
â±ââââ² (5-10% of tests)
â± â² Real browser, full stack
â±ââââââââ²
â± â² Integration Tests
â±ââââââââââââ² (15-25% of tests)
â± â² Multiple units, real dependencies
â±ââââââââââââââââ²
â± â² Unit Tests
â±ââââââââââââââââââââ² (65-80% of tests)
â± â² Single units, isolated, fast
| Level | Speed | Cost | Confidence | Isolation |
|---|---|---|---|---|
| Unit | ~1ms | Low | Narrow | High |
| Integration | ~100ms | Medium | Medium | Medium |
| E2E | ~1-10s | High | High | Low |
When to Deviate
Invert the pyramid when:
- Legacy code without unit tests â Start with E2E for safety net, add units as you refactor
- Highly integrated systems â More integration tests, fewer isolated units
- Critical user journeys â Extra E2E coverage for checkout, auth, payments
- UI-heavy apps â More component/visual tests, fewer traditional units
Decision Framework
What test type should I write?
Is it a pure function or utility?
ââ YES â Unit test
ââ NO â
Does it involve UI rendering?
ââ YES â Component test (RTL/Vue Test Utils)
ââ NO â
Does it cross system boundaries (DB, API, services)?
ââ YES â Integration test
ââ NO â
Is it a critical user flow spanning multiple pages?
ââ YES â E2E test (Playwright/Cypress)
ââ NO â Unit or integration test
What NOT to test:
- Framework internals (React hooks work, Next.js routing works)
- Third-party libraries (axios sends requests correctly)
- Implementation details (which internal method was called)
- Styling (unless visual regression is set up)
- Trivial code (getters, setters, type definitions)
Coverage Strategy
Meaningful Metrics
Coverage percentage alone is misleading. Focus on:
| Metric | Target | Why |
|---|---|---|
| Branch coverage | >80% | Ensures conditionals are tested |
| Critical path coverage | 100% | Auth, payments, data mutations |
| Error path coverage | >70% | Graceful failure handling |
| Edge case coverage | Document, not % | Known boundaries tested |
Coverage by Layer
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â UI Components Target: 70-80% â
â ââ Render states (loading, error, empty, success) â
â ââ User interactions (click, type, submit) â
â ââ Accessibility (keyboard nav, ARIA) â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â Business Logic / Services Target: 85-95% â
â ââ Happy paths â
â ââ Error cases â
â ââ Edge cases (null, empty, boundary) â
â ââ State transitions â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â Data Layer / API Target: 80-90% â
â ââ CRUD operations â
â ââ Validation â
â ââ Error handling â
â ââ Authorization â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
â E2E Flows Target: Critical â
â ââ Authentication flow â
â ââ Core purchase/conversion path â
â ââ Critical business workflows â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
What to Skip
// â Don't test pass-through functions
export const getUser = (id: string) => userService.getById(id);
// â Don't test type definitions
type User = { id: string; name: string };
// â Don't test constants
export const MAX_RETRIES = 3;
// â Don't test simple getters
get fullName() { return `${this.first} ${this.last}`; }
Test Organization
Folder Structure
src/
âââ components/
â âââ Button/
â â âââ Button.tsx
â â âââ Button.test.tsx # Co-located unit/component tests
â â âââ Button.stories.tsx # Storybook stories
â âââ Form/
â âââ Form.tsx
â âââ Form.test.tsx
âââ hooks/
â âââ useAuth.ts
â âââ useAuth.test.ts
âââ services/
â âââ userService.ts
â âââ userService.test.ts
âââ utils/
âââ validation.ts
âââ validation.test.ts
tests/ # Cross-cutting tests
âââ setup.ts # Global test setup
âââ mocks/
â âââ handlers.ts # MSW request handlers
â âââ server.ts # MSW server setup
âââ fixtures/
â âââ users.ts # Shared test data
âââ integration/ # Multi-module tests
â âââ checkout.test.ts
âââ e2e/ # Playwright/Cypress
âââ auth.spec.ts
âââ checkout.spec.ts
âââ fixtures/
âââ test-user.json
Naming Conventions
// File naming
Button.test.tsx // Co-located with component
Button.spec.tsx // Alternative (consistency matters)
button.test.ts // For non-component modules
// Test naming: describe what, not how
describe('LoginForm', () => {
// â
Describes behavior
it('shows error message when credentials are invalid', () => {});
it('redirects to dashboard after successful login', () => {});
it('disables submit button while loading', () => {});
// â Describes implementation
it('calls setError with message', () => {});
it('uses useRouter hook', () => {});
});
Anti-Patterns
Testing Implementation Details
// â Tests internal state
it('updates internal state', () => {
const { result } = renderHook(() => useCounter());
act(() => result.current.increment());
expect(result.current.state.count).toBe(1); // Testing internal state
});
// â
Tests behavior
it('displays incremented value', () => {
render(<Counter />);
fireEvent.click(screen.getByRole('button', { name: /increment/i }));
expect(screen.getByText('1')).toBeInTheDocument();
});
Snapshot Abuse
// â Snapshot everything
it('renders', () => {
expect(render(<ComplexPage />)).toMatchSnapshot();
});
// â
Snapshot sparingly, for specific output
it('generates correct email template', () => {
expect(generateEmailHTML(data)).toMatchSnapshot();
});
Test Interdependence
// â Tests depend on order
describe('User', () => {
it('creates user', () => { /* sets userId */ });
it('updates user', () => { /* uses userId from above */ });
it('deletes user', () => { /* uses userId from above */ });
});
// â
Each test is independent
describe('User', () => {
it('creates user', async () => {
const user = await createUser(userData);
expect(user.id).toBeDefined();
});
it('updates user', async () => {
const user = await createUser(userData);
const updated = await updateUser(user.id, newData);
expect(updated.name).toBe(newData.name);
});
});
Mocking Too Much
// â Mock everything = test nothing
it('processes order', () => {
jest.mock('./inventory');
jest.mock('./payment');
jest.mock('./shipping');
jest.mock('./notifications');
// What are we even testing?
});
// â
Mock boundaries, test logic
it('processes order', () => {
const mockPaymentGateway = createMockPaymentGateway();
const result = processOrder(order, mockPaymentGateway);
expect(result.status).toBe('completed');
expect(mockPaymentGateway.charge).toHaveBeenCalledWith(order.total);
});
Arbitrary Waits
// â Flaky: timing-dependent
it('shows success message', async () => {
fireEvent.click(submitButton);
await new Promise(r => setTimeout(r, 1000));
expect(screen.getByText('Success')).toBeInTheDocument();
});
// â
Reliable: wait for condition
it('shows success message', async () => {
fireEvent.click(submitButton);
await waitFor(() => {
expect(screen.getByText('Success')).toBeInTheDocument();
});
});
Quick Reference: Test Doubles
| Type | Purpose | Use When |
|---|---|---|
| Stub | Returns canned data | Need predictable responses |
| Mock | Records calls, verifiable | Need to verify interactions |
| Spy | Wraps real implementation | Need to observe real behavior |
| Fake | Working implementation | Need simplified but real behavior |
// Stub: returns fixed data
const userServiceStub = { getUser: () => ({ id: '1', name: 'Test' }) };
// Mock: verifiable
const sendEmail = vi.fn();
await processOrder(order);
expect(sendEmail).toHaveBeenCalledWith(order.email);
// Spy: observe real calls
const spy = vi.spyOn(console, 'error');
await riskyOperation();
expect(spy).not.toHaveBeenCalled();
// Fake: real-ish implementation
const fakeDb = new Map<string, User>();
const userService = createUserService({ db: fakeDb });
Design Artifact Integration
Consuming Accessibility Audits
When accessibility audits exist from the design phase, incorporate their findings into your test strategy.
Check for design artifacts:
# Look for accessibility audit in .claude/design directory
find .claude/design -name "a11y-audit.md" 2>/dev/null
If .claude/design/*/a11y-audit.md exists:
- Read the audit findings â Focus on the “Findings” section
- Extract testable requirements â Each finding with severity (Critical/Major) should have corresponding tests
- Map findings to test types:
- WCAG compliance issues â Component tests (RTL/Vitest) + E2E tests (Playwright)
- Keyboard navigation issues â E2E tests with keyboard-only simulation
- Screen reader issues â Component tests with accessibility tree assertions
- Color contrast issues â Visual regression tests or automated checks
- ARIA issues â Component tests checking roles, labels, states
Generate accessibility test cases from audit:
// Example: From finding "Form inputs missing labels"
describe('LoginForm accessibility', () => {
it('all form fields have accessible labels', () => {
render(<LoginForm />);
const emailInput = screen.getByRole('textbox', { name: /email/i });
const passwordInput = screen.getByLabelText(/password/i);
expect(emailInput).toHaveAccessibleName();
expect(passwordInput).toHaveAccessibleName();
});
});
// Example: From finding "Checkout not keyboard accessible"
test('complete checkout flow using keyboard only', async ({ page }) => {
await page.goto('/checkout');
// Tab navigation through form
await page.keyboard.press('Tab');
await page.keyboard.type('John Doe');
await page.keyboard.press('Tab');
await page.keyboard.type('john@example.com');
// Submit with Enter
await page.keyboard.press('Enter');
await expect(page.locator('[role="alert"]')).toContainText('Order confirmed');
});
// Example: From finding "Low contrast on primary buttons"
describe('Button contrast', () => {
it('meets WCAG AA contrast ratio', () => {
render(<PrimaryButton>Submit</PrimaryButton>);
const button = screen.getByRole('button', { name: /submit/i });
const styles = window.getComputedStyle(button);
// Can use jest-axe or manual color contrast calculation
expect(button).toHaveNoViolations(); // jest-axe
});
});
Mapping WCAG criteria to test approaches:
| WCAG Criterion | Test Type | Tool/Approach |
|---|---|---|
| 1.1.1 Non-text Content | Component | Check alt attributes, aria-label |
| 1.3.1 Info and Relationships | Component | Validate semantic HTML, heading hierarchy |
| 1.4.3 Contrast (Minimum) | Visual/Component | jest-axe, color contrast library |
| 2.1.1 Keyboard | E2E | Playwright keyboard navigation |
| 2.4.3 Focus Order | E2E | Tab order verification |
| 2.4.7 Focus Visible | E2E | Assert focus indicator visibility |
| 3.3.1 Error Identification | Component + E2E | Check error messages are announced |
| 4.1.2 Name, Role, Value | Component | ARIA role and attribute validation |
Backward compatibility:
If no a11y-audit.md exists, the skill works normally. Accessibility testing is still important:
- Add basic keyboard navigation tests for interactive elements
- Validate semantic HTML and ARIA where used
- Include accessibility in component test checklist (see below)
Checklist: New Feature Testing
Before Writing Tests
- Identify the test pyramid distribution for this feature
- List critical user paths that need E2E coverage
- Identify external boundaries to mock (APIs, services)
- Set up test data factories/fixtures
- Check for
.claude/design/*/a11y-audit.mdand extract testable requirements
Component Tests
- Renders correctly with default props
- Renders all visual states (loading, error, empty, success)
- Handles user interactions
- Accessibility: keyboard navigation, ARIA labels
- If a11y-audit exists: Address specific findings (labels, roles, contrast)
Integration Tests
- Happy path works end-to-end
- Error paths handled gracefully
- Edge cases (empty, null, boundary values)
- Authorization enforced
E2E Tests
- Critical path covered (auth, checkout, core workflows)
- Cross-browser if required
- Mobile viewport if responsive
- Test data cleanup after run
- If a11y-audit exists: Keyboard-only flows for critical paths
References:
- references/test-architecture.md â Folder structure, shared utilities, test configuration
- references/frontend-testing.md â Component testing with RTL and Vue Test Utils
- references/e2e-testing.md â Playwright and Cypress patterns, flakiness prevention
- references/test-design.md â Test case design, boundary analysis, equivalence partitioning
- references/mocking-strategies.md â When and how to mock, MSW patterns
- references/coverage-guide.md â Meaningful coverage metrics and targets