使用 ReactTestUtils 在 React 中模拟浏览器事件
为何模拟
在对 React 组件进行单元测试时,能够模拟浏览器事件通常很有用。这使我们能够断言事件处理程序已被调用并断言正确的参数。我们模拟浏览器事件是因为我们通常不想在测试环境中要求真实的浏览器,尤其是在单元测试中。
模拟浏览器事件的方法有很多种。我们将介绍最简单、内置且官方的方法:使用react-dom库中的ReactTestUtils。
使用 ReactTestUtils.Simulate
ReactTestUtils.Simulate模块中提供了一些实用程序,用于模拟浏览器事件,例如click和change 。此模块包含所有 React 支持的事件类型。我们将尝试两种常见的事件类型:
- ReactTestUtils.Simulate.click
- ReactTestUtils.Simulate.change
允许组件访问 DOM
浏览器事件是在 DOM 中生成的,DOM 是构成网页标记的元素。因此,ReactTestUtils.Simulate API 需要对 DOM 元素进行操作。
但是 React 组件通常会尝试忽略 DOM 并在组件级别进行操作。然而,React 有一个名为refs的机制,允许组件访问底层 DOM。
假设我们有一个名为Button的自定义组件。如果我们向其传递一个特殊的ref属性,则该属性的值将包含指向其 DOM 元素的指针。为了允许 ref传递,我们必须在Button.js文件中使用React.forwardRef API :
import React from "react";
export default React.forwardRef(function Button(props, ref) {
return <button {...props} ref={ref} />;
});
在测试中设置 DOM
现在我们将设置测试代码。我们将使用create-react-app中的标准Jest API 进行测试。在我们的测试文件中,我们将设置测试上述按钮组件。
首先,我们要把导入的东西搞定。然后我们需要为我们的组件创建一个元素来渲染它:
import React from "react";
import ReactDOM from "react-dom";
import ReactTestUtils from "react-dom/test-utils";
import Button from "./Button.js";
let div;
beforeEach(() => {
div = document.createElement("div");
});
而且,因为我们要将每个测试渲染到这个 div 中,所以我们也希望稍后清理它:
afterEach(() => {
ReactDOM.unmountComponentAtNode(div);
});
模拟点击事件
现在让我们在这个自定义按钮组件上测试一个onClick prop。onClick应该传递到组件的底层<button />元素,因为我们正在将props 传播到它( {...props} )。
让我们创建测试:
it("captures clicks", done => {
const ref = React.createRef();
function handleClick() {
done();
}
ReactDOM.render(<Button onClick={handleClick} ref={ref} />, div);
ReactTestUtils.Simulate.click(ref.current);
});
在测试中,我们创建一个 ref,以便我们可以将其传递给渲染的按钮,然后传递给Simulate.click调用。
onClick prop 获取handleClick事件处理函数的值。该函数调用done() ,它是it函数的回调。
请注意,传递此回调会使测试异步,这意味着在调用回调之前测试不会完成。如果从未调用过回调,测试将超时并失败。相反,如果调用回调,则表明按钮上的点击是模拟的,并且调用了处理程序。使用上面的代码,确实如此!
模拟变化事件
让我们尝试另一种浏览器事件模拟。这次我们将模拟一个改变事件。这种事件在文本输入字段之类的东西上很有用,我们想知道输入值何时发生变化以及当前值实际上是什么。
我们将创建一个TextInput.js组件:
import React from "react";
export default React.forwardRef(function TextInput(props, ref) {
return <input {...props} ref={ref} />;
});
请注意,它非常简单,仅仅前置一个输入元素并将一个ref和所有{...props}传递到其中。
这次测试与上次非常相似:
it("captures changes", done => {
const ref = React.createRef();
function handleChange(evt) {
expect(evt.target.value).toEqual("whamo");
done();
}
ReactDOM.render(<TextInput onChange={handleChange} ref={ref} />, div);
ReactTestUtils.Simulate.change(ref.current, { target: { value: "whamo" } });
});
请注意,现在我们调用ReactTestUtils.Simulate.change方法,并传递第二个参数。此参数是一个对象,表示Event对象。例如,它包含target属性,通常表示 Event 是从哪个元素分派的。这里我们只是存根一个值。
然后,在我们的handleChange处理程序中,我们可以断言事件目标的值作为“whamo”(始终是一个很大的输入值)。
众多方法之一
现在我们已经模拟了两种不同的浏览器事件。我们应该能够以类似的方式处理我们可能需要的其他事件。
我们刚刚探索的模拟和测试浏览器事件的方法是最简单的选择。它不需要其他库 - 它只使用 React 团队的官方库。而且效果还不错。我们能够毫不费力地完成任务。好吧,确保 refs 有点麻烦。
还有许多其他库和方法可以模拟浏览器事件。我最喜欢的一个值得一看的是React Testing Library,它提供了一些用于访问 DOM 的便捷 API,并鼓励我们在测试中使用黑盒方法。要试用此库,请参阅相关指南使用 React Testing Library 在 React 中模拟浏览器事件。
试验并尝试几种方法,然后选择一种适合您的口味和项目的方法。
要在您自己的机器上运行上述示例中的测试,请查看此demo-react-simulate-test项目。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~