Skip to main content

testing

Testing patterns for behavior-driven tests. Use when writing tests, creating test factories, structuring test files, or deciding what to test. Do NOT use for UI-specific testing (see front-end-testing or react-testing skills). Use when this capability is needed.

Ir para a instalação

Informações da origem

Repositório
tomevault-io/tomes
Última atividade na origem
23 de julho de 2026 às 21:48
Idioma detectado do SKILL.md
inglês
Estrelas
1
Forks
0

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
testing
description
Testing patterns for behavior-driven tests. Use when writing tests, creating test factories, structuring test files, or deciding what to test. Do NOT use for UI-specific testing (see front-end-testing or react-testing skills). Use when this capability is needed.
metadata
{"author":"citypaul"}
# Testing Patterns For verifying test effectiveness through mutation analysis, load the `mutation-testing` skill. For evaluating test quality against Dave Farley's properties, load the `test-design-reviewer` skill. ## Core Principle **Test behavior, not implementation.** 100% coverage through business behavior, not implementation details. **Example:** Validation code in `payment-validator.ts` gets 100% coverage by testing `processPayment()` behavior, NOT by directly testing validator functions. --- ## Test Through Public API Only Never test implementation details. Test behavior through public APIs. **Why this matters:** - Tests remain valid when refactoring - Tests document intended behavior - Tests catch real bugs, not implementation changes ### Examples ❌ **WRONG - Testing implementation:** ```typescript // ❌ Testing HOW (implementation detail) it('should call validateAmount', () => { const spy = jest.spyOn(validator, 'validateAmount'); processPayment(payment); expect(spy).toHaveBeenCalled(); // Tests HOW, not WHAT }); // ❌ Testing private methods it('should validate CVV format', () => { const result = validator._validateCVV('123'); // Private method! expect(result).toBe(true); }); // ❌ Testing internal state it('should set isValidated flag', () => { processPayment(payment); expect(processor.isValidated).toBe(true); // Internal state }); ``` ✅ **CORRECT - Testing behavior through public API:** ```typescript it('should reject negative amounts', () => { const payment = getMockPayment({ amount: -100 }); const result = processPayment(payment); expect(result.success).toBe(false); expect(result.error).toContain('Amount must be positive'); }); it('should reject invalid CVV', () => { const payment = getMockPayment({ cvv: '12' }); // Only 2 digits const result = processPayment(payment); expect(result.success).toBe(false); expect(result.error).toContain('Invalid CVV'); }); it('should process valid payments', () => { const payment = getMockPayment({ amount: 100, cvv: '123' }); const result = processPayment(payment); expect(result.success).toBe(true); expect(result.data.transactionId).toBeDefined(); }); ``` --- ## Coverage Through Behavior Validation code gets 100% coverage by testing the behavior it protects: ```typescript // Tests covering validation WITHOUT testing validator directly describe('processPayment', () => { it('should reject negative amounts', () => { const payment = getMockPayment({ amount: -100 }); const result = processPayment(payment); expect(result.success).toBe(false); }); it('should reject amounts over 10000', () => { const payment = getMockPayment({ amount: 15000 }); const result = processPayment(payment); expect(result.success).toBe(false); }); it('should reject invalid CVV', () => { const payment = getMockPayment({ cvv: '12' }); const result = processPayment(payment); expect(result.success).toBe(false); }); it('should process valid payments', () => { const payment = getMockPayment({ amount: 100, cvv: '123' }); const result = processPayment(payment); expect(result.success).toBe(true); }); }); // ✅ Result: payment-validator.ts has 100% coverage through behavior ``` **Key insight:** When coverage drops, ask **"What business behavior am I not testing?"** not "What line am I missing?" --- ## Don't Extract for Testability Never extract a function into its own file purely to give it its own unit test. Extract for readability (a descriptive name clarifies intent), for DRY (same **knowledge** used in multiple places — see the `refactoring` skill's "DRY = Knowledge, Not Code" rule), or for separation of concerns. Not for testability. If code is inline in a function, it gets coverage through that function's behavioral tests. Every layer has behavioral tests — domain functions have vitest unit tests, components have browser tests, pages have integration tests. There is no gap. The anti-pattern is creating a 1:1 mapping between extracted helpers and test files (see "No 1:1 Mapping" below). The extracted helper is an implementation detail of its consumer. Test the consumer's behavior. ❌ **WRONG — Extracted single-use helper with its own test file:** ```typescript // prepare-participant-data.ts (new file, one caller) export const prepareParticipantData = (items: Item[]) => ({ yourClaims: items.filter(i => i.isClaimed && i.isClaimedByCurrentUser), available: items.filter(i => !i.isClaimedByCurrentUser), }); // prepare-participant-data.test.ts (tests the helper directly) it('filters claims', () => { ... }); ``` ✅ **CORRECT — Inline in the consuming function, tested through its behavior:** ```typescript // load-participant-view.ts export const loadParticipantView = async (db, eventId, userId) => { const items = await getItems(db, eventId); const yourClaims = items.filter(i => i.isClaimed && i.isClaimedByCurrentUser); const available = items.filter(i => !i.isClaimedByCurrentUser); return { yourClaims, available }; }; // The behavioral test for loadParticipantView covers the filtering: it('returns claimed gifts in yourClaims and unclaimed in available', () => { const result = await loadParticipantView(db, eventId, userId); expect(result.yourClaims).toHaveLength(1); expect(result.available).toHaveLength(2); }); ``` **When extraction IS justified (DRY):** If the same filtering logic is used by multiple consumers with the same business meaning, extract it. But test it through each consumer's behavior, not as an isolated unit. --- ## Test Factory Pattern For test data, use factory functions with optional overrides. ### Core Principles 1. Return complete objects with sensible defaults 2. Accept `Partial<T>` overrides for customization 3. Validate with real schemas (don't redefine) 4. NO `let`/`beforeEach` - use factories for fresh state ### Basic Pattern ```typescript const getMockUser = (overrides?: Partial<User>): User => { return UserSchema.parse({ id: 'user-123', name: 'Test User', email: 'test@example.com', role: 'user', ...overrides, }); }; // Usage it('creates user with custom email', () => { const user = getMockUser({ email: 'custom@example.com' }); const result = createUser(user); expect(result.success).toBe(true); }); ``` ### Complete Factory Example ```typescript import { UserSchema } from '@/schemas'; // Import real schema const getMockUser = (overrides?: Partial<User>): User => { return UserSchema.parse({ id: 'user-123', name: 'Test User', email: 'test@example.com', role: 'user', isActive: true, createdAt: new Date('2024-01-01'), ...overrides, }); }; ``` **Why validate with schema?** - Ensures test data is valid according to production schema - Catches breaking changes early (schema changes fail tests) - Single source of truth (no schema redefinition) **Tip:** For factories where only a subset of fields are relevant, use `Pick<T, 'field1' | 'field2'>` for the overrides parameter to constrain what callers can customize. ### Factory Composition For nested objects, compose factories: ```typescript const getMockItem = (overrides?: Partial<Item>): Item => { return ItemSchema.parse({ id: 'item-1', name: 'Test Item', price: 100, ...overrides, }); }; const getMockOrder = (overrides?: Partial<Order>): Order => { return OrderSchema.parse({ id: 'order-1', items: [getMockItem()], // ✅ Compose factories customer: getMockCustomer(), // ✅ Compose factories payment: getMockPayment(), // ✅ Compose factories ...overrides, }); }; // Usage - override nested objects it('calculates total with multiple items', () => { const order = getMockOrder({ items: [ getMockItem({ price: 100 }), getMockItem({ price: 200 }), ], }); expect(calculateTotal(order)).toBe(300); }); ``` ### Anti-Patterns ❌ **WRONG: Using `let` and `beforeEach`** ```typescript let user: User; beforeEach(() => { user = { id: 'user-123', name: 'Test User', ... }; // Shared mutable state! }); it('test 1', () => { user.name = 'Modified User'; // Mutates shared state }); it('test 2', () => { expect(user.name).toBe('Test User'); // Fails! Modified by test 1 }); ``` ✅ **CORRECT: Factory per test** ```typescript it('test 1', () => { const user = getMockUser({ name: 'Modified User' }); // Fresh state // ... }); it('test 2', () => { const user = getMockUser(); // Fresh state, not affected by test 1 expect(user.name).toBe('Test User'); // ✅ Passes }); ``` ❌ **WRONG: Incomplete objects** ```typescript const getMockUser = () => ({ id: 'user-123', // Missing name, email, role! }); ``` ✅ **CORRECT: Complete objects** ```typescript const getMockUser = (overrides?: Partial<User>): User => { return UserSchema.parse({ id: 'user-123', name: 'Test User', email: 'test@example.com', role: 'user', ...overrides, // All required fields present }); }; ``` ❌ **WRONG: Redefining schemas in tests** ```typescript // ❌ Schema already defined in src/schemas/user.ts! const UserSchema = z.object({ ... }); const getMockUser = () => UserSchema.parse({ ... }); ``` ✅ **CORRECT: Import real schema** ```typescript import { UserSchema } from '@/schemas/user'; const getMockUser = (overrides?: Partial<User>): User => { return UserSchema.parse({ id: 'user-123', name: 'Test User', email: 'test@example.com', ...overrides, }); }; ``` --- ## Coverage Theater Detection Watch for these patterns that give fake 100% coverage: ### Pattern 1: Mock the function being tested ❌ **WRONG** - Gives 100% coverage but tests nothing: ```typescript it('calls validator', () => { const spy = jest.spyOn(validator, 'validate'); validate(payment); expect(spy).toHaveBeenCalled(); // Meaningless assertion }); ``` ✅ **CORRECT** - Test actual behavior: ```typescript it('should reject invalid payment', () => { const payment = getMockPayment({ amount: -100 }); const result = validate(payment); expect(result.success).toBe(false); expect(result.error).toContain('Amount must be positive'); }); ``` ### Pattern 2: Test only that function was called ❌ **WRONG** - No behavior validation: ```typescript it('processes payment', () => { const spy = jest.spyOn(processor, 'process'); handlePayment(payment); expect(spy).toHaveBeenCalledWith(payment); // So what? }); ``` ✅ **CORRECT** - Verify the outcome: ```typescript it('should process payment and return transaction ID', () => { const payment = getMockPayment(); const result = handlePayment(payment); expect(result.success).toBe(true); expect(result.transactionId).toBeDefined(); }); ``` ### Pattern 3: Test trivial getters/setters ❌ **WRONG** - Testing implementation, not behavior: ```typescript it('sets amount', () => { payment.setAmount(100);
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub