بنقرة واحدة
testing
Testing patterns for behavior-driven tests. Use when writing tests or test factories.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Testing patterns for behavior-driven tests. Use when writing tests or test factories.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Apply template-downstream to all registered downstream projects in bulk, one project at a time. Use when you want to push the latest .claude/, .mcp.json, or .pre-commit-config.yaml to every project at once. Triggers on: "일괄 배포", "전체 프로젝트에 내려보내기", "모든 프로젝트 업데이트", "broadcast template", "bulk sync".
The standard business voice for any Korean text a human reads — a formal-yet-friendly business register built on 해요체 but elevated with 합니다/습니다 endings, polite recommendation phrasing, natural greetings/closings, and readable structure. Use whenever producing or rewriting reader-facing text: Telegram replies, reports, READMEs, job outputs, human-facing notes, and especially when rewriting raw TIL/technical notes for sharing with colleagues. Triggers: 비즈니스 톤, business voice, 격식 있는 톤, 동료 공유용 재작성, formal rewrite, 보고서 톤, 발표자료 톤.
한국 특화 기능(교통·날씨·부동산·쇼핑·법률·금융·공공데이터 등) 요청이 들어오면 실행. git pull로 최신화 후 README 레지스트리에서 스킬을 골라 해당 SKILL.md만 읽어 진행한다.
Import Dev Sentinel experiences into localdocs/learn.sentinel.md. Use when the user wants to bring gotchas, failure lessons, or struggle experiences captured by Dev Sentinel into the project's local knowledge base. Triggers: "sentinel 가져와", "sentinel import", "gotcha 정리", "경험 가져와", "sentinel에서 배운 것", "실패 경험 가져와".
Update .claude/, .mcp.json, and .pre-commit-config.yaml from the upstream python-project-template repository into the current project. Only files that exist upstream are updated — project-only files are never deleted. Use when pulling latest tooling, rules, hooks, or skills from the canonical template into this project. Triggers on: "템플릿 최신화", "template sync", "upstream 반영", "template 업데이트".
Planning work in small, known-good increments. Use when starting significant work or breaking down complex tasks.
| name | testing |
| description | Testing patterns for behavior-driven tests. Use when writing tests or test factories. |
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.
Never test implementation details. Test behavior through public APIs.
Why this matters:
❌ WRONG - Testing implementation:
// ❌ 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:
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();
});
Validation code gets 100% coverage by testing the behavior it protects:
// 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?"
For test data, use factory functions with optional overrides.
Partial<T> overrides for customizationlet/beforeEach - use factories for fresh stateconst 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);
});
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?
For nested objects, compose factories:
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);
});
❌ WRONG: Using let and beforeEach
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
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
const getMockUser = () => ({
id: 'user-123', // Missing name, email, role!
});
✅ CORRECT: Complete objects
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
// ❌ Schema already defined in src/schemas/user.ts!
const UserSchema = z.object({ ... });
const getMockUser = () => UserSchema.parse({ ... });
✅ CORRECT: Import real schema
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,
});
};
Watch for these patterns that give fake 100% coverage:
❌ WRONG - Gives 100% coverage but tests nothing:
it('calls validator', () => {
const spy = jest.spyOn(validator, 'validate');
validate(payment);
expect(spy).toHaveBeenCalled(); // Meaningless assertion
});
✅ CORRECT - Test actual behavior:
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');
});
❌ WRONG - No behavior validation:
it('processes payment', () => {
const spy = jest.spyOn(processor, 'process');
handlePayment(payment);
expect(spy).toHaveBeenCalledWith(payment); // So what?
});
✅ CORRECT - Verify the outcome:
it('should process payment and return transaction ID', () => {
const payment = getMockPayment();
const result = handlePayment(payment);
expect(result.success).toBe(true);
expect(result.transactionId).toBeDefined();
});
❌ WRONG - Testing implementation, not behavior:
it('sets amount', () => {
payment.setAmount(100);
expect(payment.getAmount()).toBe(100); // Trivial
});
✅ CORRECT - Test meaningful behavior:
it('should calculate total with tax', () => {
const order = createOrder({ items: [item1, item2] });
const total = order.calculateTotal();
expect(total).toBe(230); // 200 + 15% tax
});
❌ WRONG - Missing edge cases:
it('validates payment', () => {
const result = validate(getMockPayment());
expect(result.success).toBe(true); // Only happy path!
});
// Missing: negative amounts, invalid CVV, missing fields, etc.
✅ CORRECT - Test all branches:
describe('validate payment', () => {
it('should reject negative amounts', () => {
const payment = getMockPayment({ amount: -100 });
expect(validate(payment).success).toBe(false);
});
it('should reject amounts over limit', () => {
const payment = getMockPayment({ amount: 15000 });
expect(validate(payment).success).toBe(false);
});
it('should reject invalid CVV', () => {
const payment = getMockPayment({ cvv: '12' });
expect(validate(payment).success).toBe(false);
});
it('should accept valid payments', () => {
const payment = getMockPayment();
expect(validate(payment).success).toBe(true);
});
});
Don't create test files that mirror implementation files.
❌ WRONG:
src/
payment-validator.ts
payment-processor.ts
payment-formatter.ts
tests/
payment-validator.test.ts ← 1:1 mapping
payment-processor.test.ts ← 1:1 mapping
payment-formatter.test.ts ← 1:1 mapping
✅ CORRECT:
src/
payment-validator.ts
payment-processor.ts
payment-formatter.ts
tests/
process-payment.test.ts ← Tests behavior, not implementation files
Why: Implementation details can be refactored without changing tests. Tests verify behavior remains correct regardless of how code is organized internally.
When writing tests, verify:
let/beforeEach - use factories for fresh state