Testing
Preact provides testing utilities in the preact/test-utils package to make testing components easier. These utilities help you control the rendering lifecycle, flush effects synchronously, and write reliable tests.
Installation
The test utilities are included in the preact package:
You’ll also want a test runner and assertion library:
Importing Test Utils
Source: test-utils/src/index.js
setupRerender()
Sets up a function to flush pending renders synchronously. By default, Preact batches renders asynchronously. In tests, you want renders to happen immediately.
How it works:
Source: test-utils/src/index.js:7-11
It replaces options.debounceRendering to capture the render callback, then returns a function to execute it synchronously.
act()
Runs a function and flushes all effects and rerenders. This is similar to React’s act() helper.
Key features:
- Flushes all pending renders
- Executes all pending effects (
useEffect, useLayoutEffect)
- Works with both sync and async callbacks
- Returns a Promise
Source: test-utils/src/index.js:27-112
Async Callbacks
act() works with asynchronous callbacks:
Source: test-utils/src/index.js:37-46, test-utils/src/index.js:97-101
Nested act() Calls
Nested act() calls are supported:
Source: test-utils/src/index.js:28-54
teardown()
Cleans up test utilities and resets Preact’s internal state. Call this after each test:
What it does:
- Flushes any pending updates
- Restores original
debounceRendering
- Cleans up internal test state
Source: test-utils/src/index.js:117-130
Always call teardown() after tests to prevent state leaking between tests.
Testing Patterns
Basic Component Test
Testing State Changes
Testing Effects
Testing Async Operations
Testing Custom Hooks
Testing Context
Testing Error Boundaries
Integration with Testing Library
Preact works seamlessly with @testing-library/preact:
Vitest Configuration
Example Vitest setup for Preact:
Source: vitest.config.mjs in Preact repo
Jest Configuration
Example Jest setup for Preact:
Snapshot Testing
Best Practices
- Always use act(): Wrap state updates and side effects in
act() to ensure all updates are flushed:
- Call teardown(): Always clean up after tests:
- Test user behavior: Focus on how users interact with your components:
- Use Testing Library queries: Prefer accessible queries:
- Mock external dependencies: Mock API calls, timers, etc.:
- Test edge cases: Don’t just test the happy path:
Debugging Tests
Using debug()
Log render count
Use preact/debug in tests for better error messages and validation.
Common Pitfalls
Forgetting to await act()
Not cleaning up
Testing implementation details