我想测试一个子组件,但它依赖于来自父组件的状态(钩子)。
孩子接收 isModalOpen
和 setIsModalOpen
作为属性。每次 isModalOpen
更改时,useEffect
中的 Child
都会触发。但是,当我尝试单独测试 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
答案 0 :(得分:2)
实际上总结一下,如果将钩子传递给孩子,那么您要测试的内容与钩子无关。将孩子视为您的对象接受测试;换句话说,它是您唯一要测试的东西。根据定义,“钩子”是无关紧要的。你传入一个函数,你可以断言一个被调用的函数具有正确的值。
总结一下,你有两种情况
为了证明这一点,我对您上面的测试进行了小幅修改。随意复制并粘贴它并自行检查,如果您有任何问题,请告诉我。
注意:从沙盒链接复制并粘贴,稍作修改,以免与原始代码产生太大差异。
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);
});