Components often use setTimeout, setInterval, or debouncing. Here's how to test them.
Jest Fake Timers
jsxtest('shows message after delay', () => { jest.useFakeTimers(); render(<DelayedMessage delay={3000} />); // Message not visible yet expect(screen.queryByText('Hello!')).not.toBeInTheDocument(); // Fast-forward time jest.advanceTimersByTime(3000); // Now visible expect(screen.getByText('Hello!')).toBeInTheDocument(); jest.useRealTimers(); });
Testing Debounced Input
jsxfunction SearchBox({ onSearch }) { const [query, setQuery] = useState(''); useEffect(() => { const timer = setTimeout(() => { if (query) onSearch(query); }, 300); return () => clearTimeout(timer); }, [query, onSearch]); return ( <input value={query} onChange={e => setQuery(e.target.value)} placeholder="Search..." /> ); } test('debounces search', async () => { jest.useFakeTimers(); const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); const onSearch = jest.fn(); render(<SearchBox onSearch={onSearch} />); await user.type(screen.getByPlaceholderText('Search...'), 'react'); // Not called immediately expect(onSearch).not.toHaveBeenCalled(); // Fast-forward past debounce jest.advanceTimersByTime(300); expect(onSearch).toHaveBeenCalledWith('react'); expect(onSearch).toHaveBeenCalledTimes(1); jest.useRealTimers(); });
userEvent with Fake Timers
jsx// Important: Configure userEvent to use fake timers const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
Testing Intervals
jsxfunction Timer() { const [seconds, setSeconds] = useState(0); useEffect(() => { const interval = setInterval(() => { setSeconds(s => s + 1); }, 1000); return () => clearInterval(interval); }, []); return <span>{seconds}s</span>; } test('increments every second', () => { jest.useFakeTimers(); render(<Timer />); expect(screen.getByText('0s')).toBeInTheDocument(); jest.advanceTimersByTime(1000); expect(screen.getByText('1s')).toBeInTheDocument(); jest.advanceTimersByTime(2000); expect(screen.getByText('3s')).toBeInTheDocument(); jest.useRealTimers(); });
Run All Timers
jsx// Run all pending timers at once jest.runAllTimers(); // Run only currently pending timers (not new ones created) jest.runOnlyPendingTimers();