当状态钩子使用 React 和 jest 传递给子组件时,如何测试副作用?



我想测试一个子组件,但它依赖于父组件的状态(钩子)。

子项接收isModalOpensetIsModalOpen作为属性。每次isModalOpen发生变化时,Child内部的useEffect都会触发。但是,当我尝试单独测试Child组件时,我无法强制状态更改触发useEffect钩子。如何在不必在测试环境中渲染Parent的情况下实现此目的?

在下面的示例中,我能够通过渲染Parent来使测试通过。我只想渲染Child,并且仍然使测试通过。

function Child({ isModalOpen, setIsModalOpen }) {
useEffect(() => {
console.log("is the modal open?", isModalOpen);
}, [isModalOpen]);
return (
<button data-testId="myBtn" onClick={() => setIsModalOpen(!isModalOpen)}>
Click to {isModalOpen ? "close" : "open"} modal
</button>
);
}
function Parent() {
const [isModalOpen, setIsModalOpen] = useState(false);
return <Child isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} />;
}

测试:

it("React test", () => {
let isModalOpen = false;
const mockFunction = jest.fn((val) => (isModalOpen = val));
const { queryByTestId } = render(
<Child isModalOpen={isModalOpen} setIsModalOpen={mockFunction} />
);
const btnEl = queryByTestId("myBtn");
btnEl.click();
expect(btnEl.innerHTML).toEqual("Click to close modal");
// -----
// -----
// -----
// ----- The below code works
// const { queryByTestId } = render(<Parent />);
// const btnEl = queryByTestId("myBtn");
// btnEl.click();
// expect(btnEl.innerHTML).toEqual("Click to close modal");
});

下面是上述代码片段的工作示例: https://codesandbox.io/s/jest-test-forked-cjm3r?file=/index.test.js

实际上总结一下,如果钩子传递给孩子,您尝试测试的内容与钩子无关。把孩子想象成你的测试对象;换句话说,这是您唯一要测试的内容。根据定义,"钩子"是无关紧要的。你传入一个函数,你可以断言一个已经用正确的值调用的函数。

总结一下,你有两种情况

当模态
  1. 打开并且您单击"单击关闭模态"按钮时,模态将关闭或以其他方式已知正在使用false调用您的模拟函数。
  2. 当模态
  3. 关闭并且您单击"单击以关闭模态"按钮时,模态将打开或以其他方式已知正在使用true调用您的模拟函数。

我对上面的测试进行了小的编辑以证明这一点。请随时复制并粘贴并自己检查,如果您有任何疑问,请告诉我。

注意:从沙盒链接复制并粘贴,稍作修改,以免与原始代码有太大差异。

it("closes the modal when clicking on the button", () => {
function Child({ isModalOpen, setIsModalOpen }) {
useEffect(() => {
console.log("is the modal open?", isModalOpen);
}, [isModalOpen]);
return (
<button data-testId="myBtn" onClick={() => setIsModalOpen(!isModalOpen)}>
Click to {isModalOpen ? "close" : "open"} modal
</button>
);
}
// notice the mock here doesn't take arguments
const mockFunction = jest.fn();
const { queryByTestId } = render(
// notice that I've set the initial value for isModal to true
<Child isModalOpen={true} setIsModalOpen={mockFunction} />
);
const btnEl = queryByTestId("myBtn");
btnEl.click();
expect(btnEl.innerHTML).toEqual("Click to close modal");
// the assertion here
expect(mockFunction).toHaveBeenCalledWith(false);
});
it("opens the modal when clicking on the bottom", () => {
function Child({ isModalOpen, setIsModalOpen }) {
useEffect(() => {
console.log("is the modal open?", isModalOpen);
}, [isModalOpen]);
return (
<button data-testId="myBtn" onClick={() => setIsModalOpen(!isModalOpen)}>
Click to {isModalOpen ? "close" : "open"} modal
</button>
);
}
const mockFunction = jest.fn();
const { queryByTestId } = render(
// notice that I've set the initial value for isModal to false
<Child isModalOpen={false} setIsModalOpen={mockFunction} />
);
const btnEl = queryByTestId("myBtn");
btnEl.click();
expect(btnEl.innerHTML).toEqual("Click to open modal");
// the assertion here
expect(mockFunction).toHaveBeenCalledWith(true);
});

相关内容

  • 没有找到相关文章

最新更新