I’m trying to test the debounce with Jest in a component I’ve made that uses URL params for a search. The handleSearch
function uses the useDebounceCallback
hook from usehooks-ts (which uses lodash debounce under the hood), and returns a Carbon component.
export function ParamSearch({
defaultSearchValue,
placeholder = 'Search',
}: {
defaultSearchValue: string;
placeholder: string;
}) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const handleSearch = useDebounceCallback((event: '' | ChangeEvent<HTMLInputElement>) => {
const { value } = event. target;
const params = new URLSearchParams(searchParams);
if (value === '') {
params. delete( 'search');
} else {
params.set('search', value);
}
router.push(${pathname}?${params.toString()});
}, 500);
return (
<TableToolbarSearch
id="search"
labelText={placeholder}
placeholder={placeholder}
defaultValue={defaultSearchValue}
onChange={handleSearch}
/>
);
}
So far, my test looks like this. The first expect
passes, but the second routerMock.push
is never called.
jest-mock('usehooks-ts', () => ({
useDebounceCallback: jest.fn((fn) => {
setTimeout (() => fn, 500)
}),
}));
describe('ParamSearch', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
it('calls handleSearch as debounced callback after delay', async () => {
usePathname.mockReturnValue('/test-search');
useSearchParams.mockReturnValue(new Map([['search', '']]));
const { getByPlaceholderText } = render(
<ParamSearch placeholder={'Search'} defaultSearchValue={''} />
);
const searchInput = getByPlaceholderText('Search');
fireEvent.change(searchInput, { target: { value: 'x'} });
expect(routerMock.push).not.toHaveBeenCalled();
jest.advanceTimersByTime(500);
expect(routerMock.push).toHaveBeenCalledWith('/test-search?search=x');
});
});
I feel like I’m doing something wrong with the fake timers, can anybody help?
2
Answers
The mock to the useDebounce hook is unnecessary if you are using jest fake timer, it should work without mocking.
Just remove these line:
And I doubt that the
setTimeout
you provided to the mock function will not be affected by jest fake timer hijack process.There is no need to mock
usehooks-ts
module anduseDebounceCallback
hook. Use the real implementation of it. You should use fake timer. Then you can advance timer like this test casee.g.
index.tsx
:index.test.tsx
:Test result: