Principles for writing effective, maintainable tests. Covers naming conventions, assertion best practices, and comprehensive edge case checklists. Based on BugMagnet by Gojko Adzic. Triggers on: writing any test, 'add tests', test review, test naming, assertion choices, edge case coverage, 'what should I test', test structure decisions.
설치
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
Principles for writing effective, maintainable tests. Covers naming conventions, assertion best practices, and comprehensive edge case checklists. Based on BugMagnet by Gojko Adzic. Triggers on: writing any test, 'add tests', test review, test naming, assertion choices, edge case coverage, 'what should I test', test structure decisions.
version
1.0.0
Writing Tests
How to write tests that catch bugs, document behavior, and remain maintainable.
Based on BugMagnet by Gojko Adzic. Adapted with attribution.
Critical Rules
🚨 Test names describe outcomes, not actions. "returns empty array when input is null" not "test null input". The name IS the specification.
🚨 Assertions must match test titles. If the test claims to verify "different IDs", assert on the actual ID values—not just count or existence.
🚨 Assert specific values, not types.expect(result).toEqual(['First.', ' Second.']) not expect(result).toBeDefined(). Specific assertions catch specific bugs.
🚨 One concept per test. Each test verifies one behavior. If you need "and" in your test name, split it.
🚨 Bugs cluster together. When you find one bug, test related scenarios. The same misunderstanding often causes multiple failures.
When This Applies
Writing new tests
Reviewing test quality
During TDD RED phase (writing the failing test)
Expanding test coverage
Investigating discovered bugs
Test Naming
Pattern:[outcome] when [condition]
Good Names (Describe Outcomes)
returns empty array when input is null
throws ValidationError when email format invalid
calculates tax correctly for tax-exempt items
preserves original order when duplicates removed
Bad Names (Describe Actions)
test null input // What about null input?
should work // What does "work" mean?
handles edge cases // Which edge cases?
email validation test // What's being validated?
The Specification Test
Your test name should read like a specification. If someone reads ONLY the test names, they should understand the complete behavior of the system.
Assertion Best Practices
Assert Specific Values
// ❌ WEAK - passes even if completely wrong dataexpect(result).toBeDefined()
expect(result.items).toHaveLength(2)
expect(user).toBeTruthy()
// ✅ STRONG - catches actual bugsexpect(result).toEqual({ status: 'success', items: ['a', 'b'] })
expect(user.email).toBe('test@example.com')
Match Assertions to Test Title
// ❌ TEST SAYS "different IDs" BUT ASSERTS COUNTit('generates different IDs for each call', () => {
const id1 = generateId()
const id2 = generateId()
expect([id1, id2]).toHaveLength(2) // WRONG: doesn't check they're different!
})
// ✅ ACTUALLY VERIFIES DIFFERENT IDsit('generates different IDs for each call', () => {
const id1 = generateId()
const id2 = generateId()
expect(id1).not.toBe(id2) // RIGHT: verifies the claim
})
Avoid Implementation Coupling
// ❌ BRITTLE - tests implementation detailsexpect(mockDatabase.query).toHaveBeenCalledWith('SELECT * FROM users WHERE id = 1')
// ✅ FLEXIBLE - tests behaviorexpect(result.user.name).toBe('Alice')
Test Structure
Arrange-Act-Assert
it('calculates total with tax for non-exempt items', () => {
// Arrange: Set up test dataconst item = { price: 100, taxExempt: false }
const taxRate = 0.1// Act: Execute the behaviorconst total = calculateTotal(item, taxRate)
// Assert: Verify the outcomeexpect(total).toBe(110)
})
One Concept Per Test
// ❌ MULTIPLE CONCEPTS - hard to diagnose failuresit('validates and processes order', () => {
expect(validate(order)).toBe(true)
expect(process(order).status).toBe('complete')
expect(sendEmail).toHaveBeenCalled()
})
// ✅ SINGLE CONCEPT - clear failuresit('accepts valid orders', () => {
expect(validate(validOrder)).toBe(true)
})
it('rejects orders with negative quantities', () => {
expect(validate(negativeQuantityOrder)).toBe(false)
})
it('sends confirmation email after processing', () => {
process(order)
expect(sendEmail).toHaveBeenCalledWith(order.customerEmail)
})
Edge Case Checklists
When testing a function, systematically consider these edge cases based on input types.