我正在尝试使用
jest
和 @testing-library/react
来验证 React 组件(包装了 HTML input
元素)是否正在使用正确的值调用它的 onChange
回调以响应 ChangeEvent。
这是反应组件
export const MyInput = ({ onChange, value }) => {
const handleChange = (event) => {
console.log(event.target.value);
onChange(event);
};
return (
<div>
<input
type="text"
onChange={handleChange}
label={"test-component"}
data-testid={"test"}
value={value}
/>
</div>
);
};
这是测试
test("onChange should work", () => {
const onChangeMock = jest.fn();
let inputValue = 1;
const { getByTestId } = render(
<MyInput onChange={onChangeMock} value={inputValue} />
);
const input = getByTestId("test");
fireEvent.change(input, { target: { value: "23" } });
expect(onChangeMock).toHaveBeenCalled();
expect(onChangeMock).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({
value: "23"
})
})
);
});
这里有一个 codesandbox 的链接,它重现了问题。
我的测试中的第二个断言失败了,因为
onChangeMock
的调用者是一个事件,其 target.value
等于 1
,而不是 23
。
更奇怪的是,当在
event.target.value
函数中记录 handleChange
时 - 它具有正确的 23
值。使用调试器进行检查还表明,笑话模拟具有正确的调用者(目标值为 23
的事件,直到调试器离开 handleChange
函数的范围)。当我回到测试范围并执行断言时,模拟事件的目标值为 1。
所以我试图理解为什么我会观察到这种行为以及造成这种行为的原因是什么?
(我假设事件处理程序范围结束时,事件的目标被设置(通过什么?)到我的 html 输入元素,并且 jest 没有捕获事件的深层克隆,因为它被传递给外部函数,但我找不到任何文档来支持这一点)
您的文本输入不会影响更改,因为您的代码没有将其视为受控输入。
简而言之,测试期待正确的事情,但组件忽略了更改。
试试这个:
import React, {useState} from "react";
export const MyInput = ({ onChange, value }) => {
const [controlledValue, setControlledValue] = useState(value);
const handleChange = (event) => {
console.log(event.target.value);
setControlledValue(event.target.value);
onChange(event);
};
return (
<div>
<input
type="text"
onChange={handleChange}
label={"test-component"}
data-testid={"test"}
value={controlledValue}
/>
</div>
);
};
React 使用 合成事件 而不是原生 DOM 事件,它们的行为相似但不完全相同,合成事件在处理程序处理程序执行后无效,为了说明这一点,请使用代码中的事件处理程序尝试一下:
const handleChange = (event) => {
console.log(event.target.value);
onChange(event); // 23
setTimeout(() => {
console.log(event.target.value);
}, 3000); // 1
};