test-levels
Test Levels Guide
Explains test types & guides test selection using car analogy.
When to Use
Invoke when:
- Explaining test concepts to team members
- Deciding which test type to write
- Reviewing test coverage strategy
- Onboarding new developers to testing
The 3 Test Levels
1. Unit Test (Test Case)
The specific instruction.
Single fn/component tested in isolation. No external deps (DB, API, browser).
| Aspect | Description |
|---|---|
| Analogy | "Check if left turn signal blinks when I push lever down" |
| Scope | Tiny detail - one fn, one input/output |
| Speed | Fast (ms) |
| Location | tests/unit/ |
When to write:
- Pure functions (formatters, validators, utils)
- Data transformations
- Business logic w/o side effects
Project example:
// tests/unit/utils/formatters.spec.ts
import {expect, test} from 'next/experimental/testmode/playwright';
import {leadingZero, formatCurrency} from '@/app/_utils/formatters';
test.describe('formatters', () => {
test('leadingZero adds zero to single-digit numbers', () => {
expect(leadingZero(7)).toBe('07');
expect(leadingZero(10)).toBe('10');
});
test('formatCurrency formats correctly', () => {
const result = formatCurrency({value: 1234.56, locale: 'en', currency: 'USD'});
expect(result).toBe('1,234.56 $');
});
});
2. Integration Test
The handshake.
Tests if 2+ parts communicate correctly. Focuses on connections, not full system.
| Aspect | Description |
|---|---|
| Analogy | "Does engine make wheels turn?" (Engine → Transmission) |
| Scope | Connections between components |
| Speed | Medium (100ms-few seconds) |
| Location | tests/integration/ |
When to write:
- Validators w/ schemas (Zod)
- API route handlers
- Service-to-service communication
- DB queries w/ mocked data
Project example:
// tests/integration/validators/offer.spec.ts
import {expect, test} from 'next/experimental/testmode/playwright';
import {mockLoggedUser} from '../../common';
import {validateCustomerStatus} from '@/app/_lib/validator';
test.beforeEach(async ({context}) => {
await mockLoggedUser(context);
});
test.describe('validate customer status', () => {
const validData = {
offerId: '670e80f0a65da593d265088a',
status: 'viewing',
};
test('returns success for valid data', async () => {
const result = validateCustomerStatus(validData);
expect(result.success).toBe(true);
expect(result.data).toEqual(validData);
});
test('returns error for invalid offerId', async () => {
const result = validateCustomerStatus({...validData, offerId: ''});
expect(result.success).toBe(false);
expect(result.error?.issues[0]?.path).toContain('offerId');
});
});
3. E2E Test (End-to-End)
The real user journey.
Full system test: browser, DB, network, 3rd-party services. Exactly as user experiences.
| Aspect | Description |
|---|---|
| Analogy | "Start car, drive to store, park, turn off" |
| Scope | Full user flow |
| Speed | Slow (seconds-minutes) |
| Location | tests/pages/ |
When to write:
- Critical user flows (login, checkout, payment)
- Multi-page journeys
- Features requiring browser interaction
- Smoke tests for deployment
Project example:
// tests/pages/start.spec.ts
import {expect, test} from 'next/experimental/testmode/playwright';
import {mockLoggedUser, resetAPIEndpointsMock} from '../common';
test.beforeEach(async ({context}) => {
await mockLoggedUser(context);
});
test.afterEach(async ({next, context}) => {
await resetAPIEndpointsMock(next);
await context.clearCookies();
});
test('start page renders correctly', async ({page}) => {
test.setTimeout(120000);
await page.goto('http://localhost:3000/start', {
timeout: 90000,
waitUntil: 'domcontentloaded'
});
await expect(page).toHaveURL('http://localhost:3000/start');
await expect(page.getByTestId('pageHeader')).toHaveClass('drop-shadow-font');
await expect(page.getByTestId('subTotal')).toContainText('Sub total');
await expect(page.getByTestId('startCounter')).toContainText('Your vacation starts in');
});
Quick Decision Guide
| Question | Test Type |
|---|---|
| "Does this fn return correct value?" | Unit |
| "Do these 2 parts work together?" | Integration |
| "Does full flow work for user?" | E2E |
Test Pyramid
/\
/E2E\ Few (slow, expensive)
/------\
/Integr- \ Some (medium)
/ ation \
/------------\
/ Unit \ Many (fast, cheap)
/________________\
Rule: More unit tests, fewer E2E tests. Unit tests catch bugs early & run fast.
Project Structure
tests/
├── unit/ # Pure fn tests (no browser)
│ └── utils/ # Utility fn tests
├── integration/ # Component interaction tests
│ ├── validators/ # Schema validation tests
│ └── lib/ # Library fn tests
├── pages/ # E2E browser tests
│ ├── start.spec.ts
│ ├── payment.spec.ts
│ └── confirm.spec.ts
├── common.ts # Shared test utilities
├── mock/ # Mock data & helpers
└── seed.spec.ts # DB seed for tests
Commands
# Run all tests (headed mode)
npm run test
# Run specific test file
npm run test tests/unit/utils/formatters.spec.ts
# Run tests in UI mode
npm run test:ui
# Show test report
npm run test:report
Best Practices
- Name tests clearly:
should [action] when [condition] - One assertion focus: Test one behavior per test
- Use test.describe: Group related tests
- Clean up: Use
afterEachfor state reset - Mock external deps: Use
mockLoggedUser,resetAPIEndpointsMock - Set timeouts: E2E tests need longer timeouts (120s)
- Use data-testid: For reliable element selection
Summary Table
| Level | Question | Scope | Speed | Location |
|---|---|---|---|---|
| Unit | "Does this fn work?" | Single fn | Fast | tests/unit/ |
| Integration | "Do parts connect?" | Connections | Medium | tests/integration/ |
| E2E | "Does flow work?" | Full system | Slow | tests/pages/ |
More from georgekhananaev/claude-skills-vault
system-architect
System architecture skill for designing scalable, maintainable software systems. Covers microservices/monolith decisions, API design, DB selection, caching, security, and scalability planning.
21skill-creator
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
12code-quality
Multi-language code quality standards and review for TypeScript, Python, Go, and Rust. Enforces type safety, security, performance, and maintainability. Use when writing, reviewing, or refactoring code. Includes review process, checklist, and Python PEP 8 deep-dive.
12token-optimizer
Reduce token count in prompts, docs, and prose. Covers prompt compression (40-60% savings), doc formatting, TOON data serialization, and Strunk's prose clarity rules. Use when compressing prompts, optimizing docs for LLM context, or writing clear technical prose.
12file-converter
Convert & transform files - images (resize, format, HEIC), markdown (PDF/HTML), data (CSV/JSON/YAML/TOML/XML), SVG, base64, text encoding. Cross-platform, single & batch mode. This skill should be used when converting file formats, resizing images, generating PDFs from markdown, or transforming data between formats.
12brainstorm
Transform ideas into fully-formed designs through collaborative dialogue. This skill should be used when brainstorming features, exploring implementation approaches, designing system architecture, or when the user has a vague idea that needs refinement. Uses incremental validation with 200-300 word sections.
11