使用 Jest 测试 React 应用程序的实用指南
已发表: 2022-03-10在本文中,我将向您介绍一个名为 Jest 的 React 测试工具,以及用于测试 React 组件的流行库 Enzyme。 我将向您介绍 Jest 测试技术,包括:运行测试、测试 React 组件、快照测试和模拟。 如果您是测试新手并且想知道如何开始,您会发现本教程很有帮助,因为我们将从测试介绍开始。 最后,您将启动并运行,使用 Jest 和 Enzyme 测试 React 应用程序。 您应该熟悉 React 才能学习本教程。
测试简介
测试是对代码如何执行的逐行审查。 应用程序的一套测试包括各种代码,以验证应用程序是否成功执行且没有错误。 当对代码进行更新时,测试也会派上用场。 更新一段代码后,您可以运行测试以确保更新不会破坏应用程序中已有的功能。
为什么要测试?
在做某事之前了解我们为什么要做某事是很好的。 那么,为什么要测试,它的目的是什么?
- 测试的第一个目的是防止回归。 回归是先前已修复的错误再次出现。 它使某个功能在某个事件发生后停止按预期运行。
- 测试确保复杂组件和模块化应用程序的功能。
- 软件应用程序或产品的有效性能需要进行测试。
测试使应用程序更健壮,更不容易出错。 这是一种验证您的代码是否按照您希望的方式运行以及您的应用程序是否按预期为您的用户工作的方法。
让我们回顾一下测试的类型以及它们的作用。
单元测试
在这种类型的测试中,测试软件的各个单元或组件。 一个单元可能是一个单独的函数、方法、过程、模块或对象。 单元测试隔离一段代码并验证其正确性,以验证软件代码的每个单元是否按预期执行。
在单元测试中,对各个程序或功能进行测试以保证它们正常运行,并且对所有组件进行单独测试。 例如,测试函数或程序中的语句或循环是否正常运行将属于单元测试的范围。
组件测试
组件测试验证应用程序各个部分的功能。 独立于其他组件对每个组件执行测试。 通常,React 应用程序由几个组件组成,因此组件测试处理单独测试这些组件。
例如,考虑一个网站,该网站具有包含许多组件的不同网页。 每个组件都有自己的子组件。 在不考虑与其他组件集成的情况下测试每个模块称为组件测试。
在 React 中进行这样的测试需要更复杂的工具。 因此,我们需要 Jest,有时还需要更复杂的工具,例如 Enzyme,稍后我们将简要讨论。
快照测试
快照测试可确保 Web 应用程序的用户界面 (UI) 不会意外更改。 它及时捕获组件的代码,以便我们可以将处于一种状态的组件与它可能采用的任何其他可能状态进行比较。
我们将在后面的部分了解快照测试。
测试的优缺点
测试很棒,应该做,但它有优点也有缺点。
优点
- 它可以防止意外的回归。
- 它允许开发人员专注于当前的任务,而不是担心过去。
- 它允许模块化构建应用程序,否则该应用程序将过于复杂而无法构建。
- 它减少了手动验证的需要。
缺点
- 您需要编写更多代码,以及调试和维护。
- 非关键测试失败可能会导致应用程序在持续集成方面被拒绝。
玩笑简介
Jest 是一个令人愉快的 JavaScript 测试框架,专注于简单性。 它可以使用 npm 或 Yarn 安装。 Jest 适合更广泛的实用程序类别,称为测试运行程序。 它适用于 React 应用程序,但它也适用于 React 应用程序之外。
Enzyme 是一个用于测试 React 应用程序的库。 它旨在测试组件,并且可以编写模拟操作以确认 UI 是否正常工作的断言。
Jest 和 Enzyme 相得益彰,因此在本文中我们将同时使用两者。
用 Jest 运行测试的过程
在本节中,我们将安装 Jest 并编写测试。 如果您是 React 新手,那么我建议您使用 Create React App,因为它已准备好使用并随 Jest 一起提供。
npm init react-app my-app
我们需要用react-test-renderer
安装 Enzyme **** 和enzyme-adapter-react-16
(数量应该基于你使用的 React 版本)。
npm install --save-dev enzyme enzyme-adapter-react-16 react-test-renderer
现在我们已经用 Jest 和 Enzyme 创建了我们的项目,我们需要在项目的src
文件夹中创建一个setupTest.js
文件。 该文件应如下所示:
import { configure } from "enzyme"; import Adapter from "enzyme-adapter-react-16"; configure({ adapter: new Adapter() });
这会导入 Enzyme 并设置适配器来运行我们的测试。
在继续之前,让我们学习一些基础知识。 本文中使用了很多关键的东西,您需要了解它们。
-
it
或test
你可以将一个函数传递给这个方法,然后测试运行器会将该函数作为一个测试块来执行。 -
describe
此可选方法用于对任意数量的it
或test
语句进行分组。 -
expect
这是测试需要通过的条件。 它将接收到的参数与匹配器进行比较。 它还使您可以访问许多匹配器,这些匹配器可以让您验证不同的事物。 您可以在文档中阅读有关它的更多信息。 -
mount
这个方法渲染整个 DOM,包括父组件的子组件,我们在其中运行测试。 -
shallow
这仅呈现我们正在测试的单个组件。 它不渲染子组件。 这使我们能够单独测试组件。
创建测试文件
Jest 如何知道什么是测试文件,什么不是? 第一条规则是在任何目录中找到的名为__test__
的任何文件都被视为测试。 如果您将 JavaScript 文件放在其中一个文件夹中,无论好坏,Jest 都会在您调用 Jest 时尝试运行它。 第二条规则是 Jest 将识别任何带有.spec.js
或.test.js
后缀的文件。 它将搜索整个存储库中所有文件夹和所有文件的名称。
让我们为为本教程创建的 React 迷你应用程序创建我们的第一个测试。 你可以在 GitHub 上克隆它。 运行npm install
安装所有包,然后npm start
启动应用程序。 检查README.md
文件以获取更多信息。
让我们打开App.test.js
来编写我们的第一个测试。 首先,检查我们的应用组件是否正确渲染以及我们是否指定了输出:
it("renders without crashing", () => { shallow(<App />); }); it("renders Account header", () => { const wrapper = shallow(<App />); const welcome = <h1>Display Active Users Account Details</h1>; expect(wrapper.contains(welcome)).toEqual(true); });
在上面的测试中,第一个测试使用shallow
,检查我们的应用组件是否正确呈现而不会崩溃。 请记住, shallow
方法仅呈现单个组件,没有子组件。
第二个测试检查我们是否在我们的应用程序组件中指定了“显示活动用户帐户”的h1
标记输出,并带有toEqual
的 Jest 匹配器。
现在,运行测试:
npm run test /* OR */ npm test
终端中的输出应如下所示:
PASS src/App.test.js √ renders without crashing (34ms) √ renders Account header (13ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 11.239s, estimated 16s Ran all test suites related to changed files. Watch Usage: Press w to show more.
如您所见,我们的测试通过了。 它表明我们有一个名为App.test.js
的测试套件,当 Jest 运行时有两个成功的测试。 稍后我们将讨论快照测试,您还将看到一个失败的测试示例。
跳过或隔离测试
跳过或隔离测试意味着当 Jest 运行时,不会运行特定的标记测试。
it.skip("renders without crashing", () => { shallow(<App />); }); it("renders Account header", () => { const wrapper = shallow(<App />); const header = <h1>Display Active Users Account Details</h1>; expect(wrapper.contains(header)).toEqual(true); });
我们的第一个测试将被跳过,因为我们使用了skip
方法来隔离测试。 因此,当 Jest 运行时,它不会运行或对我们的测试进行任何更改。 只有第二个会运行。 您也可以使用it.only()
。
对测试文件进行更改然后不得不再次手动运行npm test
有点令人沮丧。 Jest 有一个很好的功能,称为监视模式,它监视文件更改并相应地运行测试。 要在监视模式下运行 Jest,您可以运行npm test -- --watch
或jest --watch
。 在本教程的其余部分,我还建议让 Jest 在终端窗口中运行。
模拟功能
模拟是对象或模块的令人信服的副本,没有任何真正的内部运作。 它可能有一点点功能,但与真实的东西相比,它是一个模拟。 它可以由 Jest 自动创建或手动创建。
我们为什么要嘲讽? 模拟减少了依赖项的数量——即在运行测试时必须加载和解析的相关文件的数量。 因此,使用大量模拟可以使测试执行得更快。
模拟函数也称为“间谍”,因为它们让您可以监视由其他代码直接调用的函数的行为,而不仅仅是测试输出。
有两种方法可以模拟一个函数:或者通过创建一个模拟函数来在测试代码中使用它,或者通过编写一个手动模拟来覆盖模块依赖项。
手动模拟 **** 用于使用模拟数据存根功能。 例如,您可能希望创建一个允许您使用假数据的手动模拟,而不是访问远程资源(如网站或数据库)。
我们将在下一节中使用模拟函数。
测试 React 组件
本节将结合我们迄今为止在理解如何测试 React 组件方面获得的所有知识。 测试涉及确保组件的输出没有意外更改为其他内容。 以正确的方式构建组件是迄今为止确保成功测试的最有效方法。
我们可以做的一件事是测试组件的 props — 具体来说,测试一个组件的 props 是否正在传递给另一个组件。 Jest 和 Enzyme API 允许我们创建一个模拟函数来模拟 props 是否在组件之间传递。
我们必须将用户帐户道具从主App
组件传递到Account
组件。 我们需要将用户帐户详细信息提供给Account
以呈现用户的活动帐户。 这就是模拟派上用场的地方,使我们能够使用假数据测试我们的组件。
让我们为user
道具创建一个模拟:
const user = { name: "Adeneye David", email: "[email protected]", username: "Dave", };
我们在我们的测试文件中创建了一个手动模拟函数并将其包装在组件周围。 假设我们正在测试一个大型用户数据库。 不建议直接从我们的测试文件访问数据库。 相反,我们创建了一个模拟函数,它使我们能够使用假数据来测试我们的组件。
describe(" ", () => { it("accepts user account props", () => { const wrapper = mount(<Account user={user} />); expect(wrapper.props().user).toEqual(user); }); it("contains users account email", () => { const wrapper = mount(<Account user={user} />); const value = wrapper.find("p").text(); expect(value).toEqual("[email protected]"); }); });
describe(" ", () => { it("accepts user account props", () => { const wrapper = mount(<Account user={user} />); expect(wrapper.props().user).toEqual(user); }); it("contains users account email", () => { const wrapper = mount(<Account user={user} />); const value = wrapper.find("p").text(); expect(value).toEqual("[email protected]"); }); });
我们上面有两个测试,我们使用了一个describe
层,它接受被测试的组件。 通过指定我们期望通过测试通过的道具和值,我们可以继续。
在第一个测试中,我们检查传递给挂载组件的 props 是否等于我们在上面创建的模拟 props。
对于第二个测试,我们将 user 属性传递给挂载的Account
组件。 然后,我们检查是否可以找到与Account
组件中的内容相对应的<p>
元素。 当我们运行测试套件时,您会看到测试运行成功。
我们还可以测试组件的状态。 让我们检查错误消息的状态是否等于 null:
it("renders correctly with no error message", () => { const wrapper = mount( ); expect(wrapper.state("error")).toEqual(null); });
it("renders correctly with no error message", () => { const wrapper = mount( ); expect(wrapper.state("error")).toEqual(null); });
在此测试中,我们使用toEqual()
匹配器检查组件错误的状态是否等于 null。 如果我们的应用程序中有错误消息,则测试将在运行时失败。
在下一节中,我们将介绍如何使用快照测试来测试 React 组件,这是另一种惊人的技术。
快照测试
快照测试在某个时刻捕获组件的代码,以便将其与存储在测试旁边的参考快照文件进行比较。 它用于跟踪应用程序 UI 的变化。
快照的实际代码表示是一个 JSON 文件,并且该 JSON 包含创建快照时组件外观的记录。 在测试期间,Jest 将此 JSON 文件的内容与测试期间组件的输出进行比较。 如果它们匹配,则测试通过; 如果他们不这样做,则测试失败。
要将 Enzyme 包装器转换为与 Jest 快照测试兼容的格式,我们必须安装enzyme-to-json
:
npm install --save-dev enzyme-to-json
让我们创建我们的快照测试。 当我们第一次运行它时,该组件代码的快照将被组合并保存在src
目录中的一个新的__snapshots__
文件夹中。
it("renders correctly", () => { const tree = shallow(<App />); expect(toJson(tree)).toMatchSnapshot(); });
当上面的测试成功运行时,当前的 UI 组件将与现有的进行比较。
现在,让我们运行测试:
npm run test
当测试套件运行时,将生成一个新的快照并将其保存到__snapshots__
文件夹中。 当我们随后运行测试时,Jest 会检查组件是否与快照匹配。
如上一节所述,Enzyme 包中的shallow
方法用于渲染单个组件,仅此而已。 它不渲染子组件。 相反,它为我们提供了一种很好的方式来隔离代码并在调试时获得更好的信息。 另一种名为mount
的方法用于渲染完整的 DOM,包括我们正在运行测试的父组件的子组件。
我们还可以更新我们的快照,让我们对我们的组件进行一些更改以使我们的测试失败,这会发生,因为该组件不再对应于我们在快照文件中的内容。 为此,让我们将组件中的<h3>
标签从<h3> Loading...</h3>
更改为<h3>Fetching Users...</h3>
。 当测试运行时,我们将在终端中得到以下内容:
FAIL src/App.test.js (30.696s) × renders correctly (44ms) ● renders correctly expect(received).toMatchSnapshot() Snapshot name: `renders correctly 1 - Snapshot + Received
7 | it("正确渲染", () => { 8 | 常量包装器 = 浅( FAIL src/App.test.js (30.696s) × renders correctly (44ms) ● renders correctly expect(received).toMatchSnapshot() Snapshot name: `renders correctly 1 - Snapshot + Received
显示活动用户帐户详细信息
- 加载中... + 获取用户...
); > 9 | 期望(toJson(包装器)).toMatchSnapshot(); | ^ 10 | }); 11 | 12 | /* it("渲染不会崩溃", () => { 在对象。 (src/App.test.js:9:27) › 1 个快照失败。 快照摘要 › 1 个测试套件的 1 个快照失败。 检查您的代码更改或按 `u` 更新它们。 测试套件:1 个失败,总共 1 个 测试:1 次失败,总共 1 次 快照:1 个失败,共 1 个 时间:92.274s 运行与更改的文件相关的所有测试套件。 观看用法:按 w 显示更多。
如果我们希望我们的测试通过,我们要么将测试更改为之前的状态,要么更新快照文件。 在命令行中,Jest 提供有关如何更新快照的说明。 首先,在命令行中按w
显示更多内容,然后按u
更新快照。
› Press u to update failing snapshots.
当我们按u
更新快照时,测试将通过。
结论
我希望您喜欢本教程。 我们已经使用 Enzyme 测试库学习了一些 Jest 测试技术。 我还向您介绍了运行测试、测试 React 组件、模拟和快照测试的过程。 如果您有任何问题,可以将它们留在下面的评论部分,我很乐意回答每个问题并与您一起解决任何问题。
资源和进一步阅读
- 笑话文档
- 酶文档
- “如何测试 React 组件:完整指南”,Mohammad Iqbal,freeCodeCamp
- “用 Jest 和 Enzyme 测试反应”,Dominic Fraser,CodeClan