使用现代假计时器(Jest 27 已经默认),您可以更简洁地对其进行测试:
import debounce from "lodash.debounce";
describe("debounce", () => {
beforeEach(() => {
jest.useFakeTimers("modern");
});
afterEach(() => {
jest.useRealTimers();
});
it("should work properly", () => {
const callback = jest.fn();
const debounced = debounce(callback, 500);
debounced();
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(100);
debounced();
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(499);
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(1);
expect(callback).toBeCalledTimes(1);
});
it("should fire with lead", () => {
const callback = jest.fn();
const debounced = debounce(callback, 500, { leading: true });
expect(callback).not.toBeCalled();
debounced();
expect(callback).toBeCalledTimes(1);
jest.advanceTimersByTime(100);
debounced();
expect(callback).toBeCalledTimes(1);
jest.advanceTimersByTime(499);
expect(callback).toBeCalledTimes(1);
jest.advanceTimersByTime(1);
expect(callback).toBeCalledTimes(2);
});
});
您可以将其实现为像这样去抖动的状态钩子......
import debounce from "lodash.debounce";
import { Dispatch, useCallback, useState } from "react";
export function useDebouncedState<S>(
initialValue: S,
wait: number,
debounceSettings?: Parameters<typeof debounce>[2]
): [S, Dispatch<S>] {
const [state, setState] = useState<S>(initialValue);
const debouncedSetState = useCallback(
debounce(setState, wait, debounceSettings),
[wait, debounceSettings]
);
return [state, debouncedSetState];
}
并测试为
/**
* @jest-environment jsdom
*/
import { act, render, waitFor } from '@testing-library/react';
import React from 'react';
import { useDebouncedState } from "./useDebouncedState";
describe("useDebounceState", () => {
beforeEach(() => {
jest.useFakeTimers("modern");
});
afterEach(() => {
jest.useRealTimers();
});
it("should work properly", async () => {
const callback = jest.fn();
let clickCount = 0;
function MyComponent() {
const [foo, setFoo] = useDebouncedState("bar", 500);
callback();
return <div data-testid="elem" onClick={() => { ++clickCount; setFoo("click " + clickCount); }}>{foo}</div>
}
const { getByTestId } = render(<MyComponent />)
const elem = getByTestId("elem");
expect(callback).toBeCalledTimes(1);
expect(elem.textContent).toEqual("bar");
jest.advanceTimersByTime(100);
elem.click();
expect(callback).toBeCalledTimes(1);
expect(elem.textContent).toEqual("bar");
jest.advanceTimersByTime(399);
expect(callback).toBeCalledTimes(1);
expect(elem.textContent).toEqual("bar");
act(() => jest.advanceTimersByTime(1));
await waitFor(() => {
expect(callback).toBeCalledTimes(2);
expect(elem.textContent).toEqual("click 1");
});
elem.click();
await waitFor(() => {
expect(callback).toBeCalledTimes(2);
expect(elem.textContent).toEqual("click 1");
});
act(() => jest.advanceTimersByTime(500));
await waitFor(() => {
expect(callback).toBeCalledTimes(3);
expect(elem.textContent).toEqual("click 2");
});
});
});
源代码在https://github.com/trajano/react-hooks-tests/tree/master/src/useDebouncedState