Avoid Nesting When You're Testing
Nested describe blocks with beforeEach hooks create cognitive burden by requiring developers to track mutable state across scope levels—prefer inline setup or factory functions instead.
Summary
Kent C. Dodds argues against using nested describe blocks with beforeEach hooks for code reuse in tests. The pattern creates cognitive overhead: developers must mentally track variable assignments across multiple scope levels. "Where is handleSubmit coming from and what's its value?" becomes a frustrating question when variables are assigned in outer hooks and reassigned in nested ones.
Key Concepts
- Over-abstraction harms tests: Utility functions like
changeUsernameInput()add indirection without proportional benefits for simple scenarios - Mutable state breeds confusion: Variable reassignments across nested scopes reduce cognitive capacity for actual test logic
- Duplication beats wrong abstraction: Explicit, self-contained tests are more maintainable than DRY tests with hidden state
Code Snippets
The Problem: Nested State
Variables assigned in outer beforeEach blocks become difficult to trace.
describe('User Form', () => {
let handleSubmit
beforeEach(() => {
handleSubmit = jest.fn()
})
describe('when submitted', () => {
beforeEach(() => {
// Where did handleSubmit come from? What's its current value?
})
})
})
The Solution: Setup Functions
Return objects containing test utilities through composition.
function setupSuccessCase() {
const handleSubmit = jest.fn()
const utils = render(<Form onSubmit={handleSubmit} />)
// ... perform actions
return { ...utils, handleSubmit }
}
test('submits form with valid data', () => {
const { handleSubmit } = setupSuccessCase()
expect(handleSubmit).toHaveBeenCalled()
})
When Hooks Are Appropriate
beforeAll/afterAll have valid uses:
- Server startup and shutdown
- Mocking
console.erroracross test suites
The key: pair setup with cleanup, don't use hooks as code-reuse mechanisms.
Connections
No directly related notes yet. This article establishes foundational testing philosophy.