如何断言包裹在 debounce 中的函数被调用
How to assert function wrapped in debounce is called
我有一个呈现输入的 React 组件。当输入更改时,将调用通过 props 传递的更改处理程序的去抖动版本。
我试图断言更改处理程序已被调用,但根据 Jest 的说法,事实并非如此。
包裹输入
import debounce from 'lodash.debounce';
const WrappedInput = ({ data, onChange }) => {
const [value, setValue] = useState(data.value);
const handleSave = useCallback(
value =>
debounce(() => {
onChange({
value,
});
}, 500),
[onChange]
);
const handleChange = useCallback(
event => {
setValue(event.target.value);
handleSave(event.target.value);
},
[saveChange]
);
return (
<div data-testid="input-container">
<Input
value={value}
onChange={handleChange}
/>
</div>
);
};
WrappedInput.test
test('save occurs', async () => {
const onChange = jest.fn(() => {});
const { getByTestId } = renderWrappedInput({ data: {}, onChange });
const input = await waitFor(() => getByTestId('input-container').querySelector('input'));
fireEvent.change(input, { target: { value: 'asdf' } });
expect(onChange).toBeCalledTimes(1);
});
我收到错误 Expected number of calls: 1 \n Received number of calls: 0
。我还尝试通过在 onChange
声明下方添加 jest.mock('lodash.debounce', () => () => onChange());
来模拟 lodash.debouce
并得到相同的错误。
您需要在测试时删除去抖动,或者使用假定时器,这样您就不需要等待实际的去抖动时间。 Jest 自己的假计时器似乎不适用于 Lodash 的去抖动。我发现 @sinonjs/fake-timers
有效。您需要在测试之前安装假计时器,然后再卸载它。这将用您控制的假货替换 setTimeout
、Date
等。
import FakeTimers from '@sinonjs/fake-timers'
let clock
beforeEach(() => {
clock = FakeTimers.install()
})
afterEach(() => {
clock.uninstall()
})
test('save occurs', async () => {
const onChange = jest.fn(() => {});
const { getByTestId } = renderWrappedInput({ data: {}, onChange });
const input = await waitFor(() => getByTestId('input-container').querySelector('input'));
fireEvent.change(input, { target: { value: 'asdf' } });
clock.tick(500) // advance the clock by the amount of the debounce
expect(onChange).toBeCalledTimes(1);
});
如果你的 React 组件需要做一些异步的事情,即需要等待一个承诺来解决,使用 await clock.tickAsync()
而不是 clock.tick()
。假计时器的 ***Async 变体打破事件循环并让承诺执行。
作为 side-note,您可能希望使用 @testing-library/user-event
而不是 fireEvent
- 它公开了更高级别 API 用于单击、键入、悬停等在。它更准确地模拟真实打字 - 例如,它首先点击控件,并且在击键之间有可选的延迟。
import userEvent from '@testing-library/user-event'
// ..
// fireEvent.change(input, { target: { value: 'asdf' } });
// ↓
userEvent.type(input, 'asdf')
我有一个呈现输入的 React 组件。当输入更改时,将调用通过 props 传递的更改处理程序的去抖动版本。
我试图断言更改处理程序已被调用,但根据 Jest 的说法,事实并非如此。
包裹输入
import debounce from 'lodash.debounce';
const WrappedInput = ({ data, onChange }) => {
const [value, setValue] = useState(data.value);
const handleSave = useCallback(
value =>
debounce(() => {
onChange({
value,
});
}, 500),
[onChange]
);
const handleChange = useCallback(
event => {
setValue(event.target.value);
handleSave(event.target.value);
},
[saveChange]
);
return (
<div data-testid="input-container">
<Input
value={value}
onChange={handleChange}
/>
</div>
);
};
WrappedInput.test
test('save occurs', async () => {
const onChange = jest.fn(() => {});
const { getByTestId } = renderWrappedInput({ data: {}, onChange });
const input = await waitFor(() => getByTestId('input-container').querySelector('input'));
fireEvent.change(input, { target: { value: 'asdf' } });
expect(onChange).toBeCalledTimes(1);
});
我收到错误 Expected number of calls: 1 \n Received number of calls: 0
。我还尝试通过在 onChange
声明下方添加 jest.mock('lodash.debounce', () => () => onChange());
来模拟 lodash.debouce
并得到相同的错误。
您需要在测试时删除去抖动,或者使用假定时器,这样您就不需要等待实际的去抖动时间。 Jest 自己的假计时器似乎不适用于 Lodash 的去抖动。我发现 @sinonjs/fake-timers
有效。您需要在测试之前安装假计时器,然后再卸载它。这将用您控制的假货替换 setTimeout
、Date
等。
import FakeTimers from '@sinonjs/fake-timers'
let clock
beforeEach(() => {
clock = FakeTimers.install()
})
afterEach(() => {
clock.uninstall()
})
test('save occurs', async () => {
const onChange = jest.fn(() => {});
const { getByTestId } = renderWrappedInput({ data: {}, onChange });
const input = await waitFor(() => getByTestId('input-container').querySelector('input'));
fireEvent.change(input, { target: { value: 'asdf' } });
clock.tick(500) // advance the clock by the amount of the debounce
expect(onChange).toBeCalledTimes(1);
});
如果你的 React 组件需要做一些异步的事情,即需要等待一个承诺来解决,使用 await clock.tickAsync()
而不是 clock.tick()
。假计时器的 ***Async 变体打破事件循环并让承诺执行。
作为 side-note,您可能希望使用 @testing-library/user-event
而不是 fireEvent
- 它公开了更高级别 API 用于单击、键入、悬停等在。它更准确地模拟真实打字 - 例如,它首先点击控件,并且在击键之间有可选的延迟。
import userEvent from '@testing-library/user-event'
// ..
// fireEvent.change(input, { target: { value: 'asdf' } });
// ↓
userEvent.type(input, 'asdf')