在 React 组件中测试异步功能
介绍
React 应用程序中的大多数功能都是异步的。测试异步功能通常很困难,但幸运的是,有一些工具和技术可以简化 React 应用程序的测试。
本指南将使用Jest和React Testing Library以及Enzyme来测试两个简单组件。代码将在组件中使用async和await运算符,但即使没有它们,也可以使用相同的技术。
第一个组件接受一个返回promise的函数作为其get prop。单击按钮时会调用此函数并显示其返回的结果。此组件的代码为:
const DisplayData = ({ get }) => {
const [display, setDisplay] = React.useState(null);
const getData = async () => {
try {
const data = await get();
setDisplay(data);
} catch (err) {
setDisplay("**** ERROR ****");
}
};
return (
<>
<button type="button" onClick={getData} aria-label="get data">
Get data
</button>
{display && (
<div className="display" aria-label="display">
{display}
</div>
)}
</>);
};
在按钮的onClick事件中,调用get函数,当 promise 返回时,显示状态要么设置为结果,要么显示错误消息。
第二个组件在安装后将等待 20 秒,然后显示一条消息。此组件的代码为:
const TimerMessage = () => {
const [message, setMessage] = React.useState(null);
React.useEffect(() => {
setTimeout(() => setMessage("Hello"), 20000);
}, []);
return (
<div>
{message && (
<div className="message" aria-label="Message">
{message}
</div>
)}
</div>);
};
Effect hook以空数组作为依赖参数进行调用,这意味着它将在组件挂载时执行。对setTimeout的调用将等待 20 秒,然后设置消息状态。
测试异步函数
为了测试第一个组件,我们需要提供一个将返回承诺的模拟函数。我们将使用jest.fn创建两个模拟:一个将承诺解析为结果,另一个拒绝承诺以测试错误情况。这些模拟的代码如下所示:
const successResult = "Some data";
const getSuccess = jest.fn(() => Promise.resolve(successResult));
const getFail = jest.fn(() => Promise.reject(new Error()));
为了使用React Testing Library测试组件,我们使用render函数,将其中一个模拟函数作为get prop 传递,并使用对象解构从返回值中获取getByLabelText和queryByLabelText函数。首先,我们使用queryByLabelText尝试获取用于显示结果的div ;由于显示状态尚未设置,因此此时它应该为null :
const { getByLabelText, queryByLabelText } = render(<DisplayData get={getSuccess} />);
const labelBeforeGet = queryByLabelText(/display/i);
expect(labelBeforeGet).toBeNull();
然后我们在按钮上触发一个 click 事件以调用get函数。这最终将设置显示状态并更新div;但是,如果我们尝试立即获取该div,它仍将为null ,因为代码正在等待 get承诺返回。为了等待这一点,我们可以使用waitForElement函数,顾名思义,它会等到元素存在于 DOM 中后才返回;实际上,它会等待最多四秒钟,如果元素仍然不存在,则会引发错误。一旦元素存在,我们就可以测试它是否包含结果或错误消息,具体取决于传入了哪个模拟:
const button = getByLabelText(/get data/i);
fireEvent.click(button);
const labelAfterGet = await waitForElement(() => queryByLabelText(/display/i));
expect(labelAfterGet.textContent).toEqual(successResult);
const wrapper = shallow(<DisplayData get={getSuccess} />);
与前面的例子一样,我们验证按钮点击前显示div不存在:
const displayDivBeforeClick = wrapper.find(".display");
expect(displayDivBeforeClick.exists()).toBe(false);
我们模拟一下按钮点击:
const getButton = wrapper.find("button");
getButton.simulate("click");
如前所述,Enzyme无法等待元素添加。因此,为了使承诺返回,我们可以使用setImmediate函数,然后在返回后测试组件:
return new Promise(resolve => setImmediate(resolve)).then(() => {
const displayDivAfterClick = wrapper.find(".display");
expect(displayDivAfterClick.exists()).toBe(true);
expect(displayDivAfterClick.text()).toEqual(successResult);
});
这些测试的代码在这里。
测试计时器
为了测试第二个组件,我们可以编写一个测试,等待二十秒,然后验证状态是否已更新,但编写需要这么长时间才能执行的测试通常不是好习惯。为了确保测试在可接受的时间内运行,我们可以使用jest 伪计时器,这将允许测试让setTimeout立即执行回调。
首先,我们需要调用jest.useFakeTimers()来确保我们使用假计时器。然后,我们可以使用React Testing Library创建组件:
const { queryByLabelText } = render(<TimerMessage />);
在Enzyme中测试此组件时,我们不能像上一个组件那样使用浅渲染。仅当组件实际安装到 DOM 中时,计时器才会执行,这是使用mount函数完成的:
const wrapper = mount(<TimerMessage />);
接下来,我们需要强制计时器完成并执行回调;我们通过调用jest.runAllTimers()来执行此操作。回调现在应该已经更新了状态,因此应该显示消息。在React Testing Library中验证这一点:
const afterTimer = queryByLabelText(/message/i);
expect(afterTimer.textContent).toEqual("Hello");
在酶中:
const afterTimer = wrapper.text();
expect(afterTimer).toBe("Hello");
</di免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~