你将要创建什么
对于许多开发人员来说,测试代码是一种令人困惑的做法。这是可以理解的,因为编写测试需要更多的努力、时间和预见可能用例的能力。由于缺乏资源和人力,从事小型项目的初创公司和开发人员通常倾向于完全忽略测试。
但是,我认为您应该测试您的组件有几个原因:
它使您对自己的代码更有信心。
测试可以提高您的工作效率。
react 也没有什么不同。当您的整个应用程序开始变成一堆难以维护的组件时,测试提供了稳定性和一致性。从第一天开始编写测试将帮助您编写更好的代码、轻松发现错误并保持更好的开发工作流程。
在本文中,我将带您了解为 React 组件编写测试所需了解的所有内容。我还将介绍一些最佳实践和技术。让我们开始吧!
在 React 中测试组件
测试是验证我们的测试断言是否真实以及它们在应用程序的整个生命周期中保持真实的过程。测试断言是一个布尔表达式,除非代码中存在错误,否则它会返回 true。
例如,一个断言可以像这样简单:“当用户导航到/login#login
时,应该呈现一个带有 id 的模式。” 因此,如果事实证明您以某种方式弄乱了登录组件,则断言将返回 false。断言不仅限于渲染的内容——您还可以断言应用程序如何响应用户交互和其他操作。
前端开发人员使用许多自动化测试策略来测试他们的代码。我们将讨论仅限于 React 流行的三种软件测试范式:单元测试、功能测试和集成测试。
单元测试
单元测试是测试界仍然流行的测试老手之一。顾名思义,您将测试各个代码片段以验证它们是否按预期独立运行。由于 React 的组件架构,单元测试是天作之合。它们也更快,因为您不必依赖浏览器。
单元测试可帮助您孤立地考虑每个组件并将它们视为函数。您对特定组件的单元测试应回答以下问题:
有道具吗?如果是,它对他们有什么作用?
它渲染了哪些组件?
它应该有一个状态吗?它应该何时或如何更新状态?
是否有安装或卸载时或用户交互时应遵循的程序?
功能测试
功能测试用于测试应用程序一部分的行为。功能测试通常是从用户的角度编写的。一项功能通常不限于单个组件。它可以是完整的表单或整个页面。
例如,当您构建注册表单时,它可能涉及表单元素、警报和错误(如果有)的组件。提交表单后呈现的组件也是该功能的一部分。这不需要浏览器渲染器,因为我们将使用内存中的虚拟dom进行测试。
集成测试
集成测试是一种测试策略,将所有单个组件作为一个组进行测试。集成测试试图通过在实际浏览器上运行测试来复制用户体验。这比功能测试和单元测试要慢得多,因为每个测试套件都是在实时浏览器上执行的。
在 React 中,单元测试和功能测试比集成测试更受欢迎,因为它们更容易编写和维护。这就是我们将在本教程中介绍的内容。
了解你的工具
您需要某些工具和依赖项来开始对您的 React 应用程序进行单元和功能测试。我在下面列出了它们。
有一个测试框架
Jest 是一个测试框架,需要零配置,因此易于设置。它比 Jasmine 和 Mocha 等测试框架更受欢迎,因为它是由 Facebook 开发的。Jest 也比其他的更快,因为它使用了一种巧妙的技术来并行化跨工作人员的测试运行。除此之外,每个测试都在沙盒环境中运行,以避免两个连续测试之间发生冲突。
如果您使用的是 create-react-app,它会随 Jest 一起提供。如果没有,您可能必须安装 Jest 和其他一些依赖项。您可以在官方Jest 文档页面上阅读更多相关信息。
反应测试渲染器
即使你使用 create-react-app,你也需要安装这个包来渲染快照。快照测试是 Jest 库的一部分。因此,您可以使用测试渲染器从虚拟 DOM 快速生成可序列化的 html 输出,而不是渲染整个应用程序的 UI。您可以按如下方式安装它:
yarn add react-test-renderer
ReactTestUtils 和酶
react-dom/test-utils由 React 团队提供的一些测试工具组成。或者,您可以使用Airbnb 发布的Enzyme包。Enzyme 比 ReactTestUtils 好很多,因为它很容易断言、操作和遍历你的 React 组件的输出。我们将使用 React utils 开始我们的测试,然后过渡到 Enzyme。
要安装 Enzyme,请运行以下命令。
yarn add enzyme enzyme-adapter-react-16
将代码添加到src/SetupTests.js。
import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; configure({ adapter: new Adapter() });
在 create-react-app 页面的测试组件 部分 有更多信息 。
设置演示应用程序并组织测试
我们将为一个简单的演示应用程序编写测试,该应用程序显示产品列表的主/详细视图。您可以在我们的GitHub 存储库中找到演示应用程序。该应用程序由一个称为容器组件ProductContainer
和三个表示组件组成ProductList
:ProductDetails
, 和ProductHeader
.
目录结构
. ├── package-lock.json ├── package.json ├── public │ ├── index.html │ └── manifest.json ├── src │ ├── components │ │ ├── App.js │ │ ├── ProductContainer.js │ │ ├── ProductDetails.jsx│ │ ├── ProductHeader.js │ │ ├── ProductList.jsx │ ├── index.js │ └── style.css
这个演示是单元测试和功能测试的一个很好的候选。您可以单独测试每个组件和/或整体测试产品列表功能。
下载演示后,创建一个名为__tests__的目录 在/src/components/里面。然后,您可以将与此功能相关的所有测试文件存储在 __tests__目录中。测试人员通常将他们的测试文件命名为.spec.js或.test.js ——例如 ProductHeader.test.js或ProductHeader.spec.js。
在 React 中编写基本测试
如果您还没有创建ProductHeader.test.js文件。这是我们的测试基本上看起来的样子:
src/components/__tests__/ProductList.test.js
describe('ProductHeader', () => { it('passing test', () => { expect(true).toBeTruthy(); }) it('failing test', () => { expect(false).toBeTruthy(); }) })
测试套件以一个describe
块开始,它是一个接受两个参数的全局 Jest 函数。第一个参数是测试套件的标题,第二个参数是实际的实现。测试套件中的每个都it()
对应一个测试或规范。测试包含一个或多个检查代码状态的期望。
expects(true).toBeTruthy();
在 Jest 中,期望是返回 true 或 false 的断言。当规范中的所有断言都为真时,就说它通过了。否则,称测试失败。
例如,我们创建了两个测试规范。第一个显然应该通过,第二个应该失败。
注意: toBeTruthy()
是一个预定义的匹配器。在 Jest 中,每个匹配器都会在期望值和实际值之间进行比较,并返回一个布尔值。还有更多可用的匹配器,我们稍后会介绍它们。
运行测试套件
create-react-app 已经设置了执行测试套件所需的一切。您需要做的就是运行以下命令:
yarn test
您应该看到如下内容:
要使失败的测试通过,您必须将匹配器替换为.toBeTruthy()
toBeFalsy()
expects(false).toBeFalsy();
就是这样!
在 Jest 中使用匹配器
如前所述,Jest 使用匹配器来比较值。您可以使用它来检查相等性、比较两个数字或字符串以及验证表达式的真实性。这是 Jest 中可用的流行匹配器列表。
toBe();
toBeNull()
toBeDefined()
toBeUndefined()
toBeTruthy()
toBeFalsy()
toBeGreaterThan()
toBeLesserThan()
toMatch()
toContain()
这只是一种味道。您可以在参考文档中找到所有可用的匹配器。
测试一个 React 组件
首先,我们将为ProductHeader
组件编写几个测试。如果您还没有 打开ProductHeader.js 文件,请打开它。
src/components/ProductHeader.js
import React, {Component} from 'react'; class ProductHeader extends Component { render() { return( <h2 className="title"> Product Listing Page </h2> ); } }; export default ProductHeader;
你想知道为什么我在这里使用类组件而不是函数组件吗?原因是使用 ReactTestUtils 更难测试功能组件。如果您想知道为什么, 这个 Stack Overflow 讨论会给出答案。
我们可以使用以下假设编写测试:
该组件应该呈现一个
h2
标签。该
h2
标签应该有一个名为 的类title
。
要渲染组件并检索相关的 DOM节点,我们需要 ReactTestUtils。删除虚拟规范并添加以下代码:
src/components/__tests__/ProductHeader.test.js
import React from 'react'; import ReactTestUtils from 'react-dom/test-utils'; import ProductsList from '../ProductsList'; describe('ProductHeader Component', () => { it('has an h2 tag', () => { //Test here }); it('is wrapped inside a title class', () => { //Test here }) })
要检查节点是否存在h2
,我们首先需要将 React 元素渲染到文档中的 DOM 节点中。您可以借助. ReactTestUtils
例如,要渲染我们的<ProductHeader/>
组件,您可以执行以下操作:
const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);
然后,您可以h2
借助 findRenderedDOMComponentWithTag('tag-name')
. 它检查所有子节点并找到与 tag-name
.
这是整个测试规范。
it('has an h2 tag', () => { const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>); var h2 = ReactTestUtils.findRenderedDOMComponentWithTag( component, 'h2' ); });
尝试保存它,您的测试运行程序应该会告诉您测试已通过。这有点令人惊讶,因为我们没有expect()
像前面示例中那样的语句。ReactTestUtils导出的大多数方法都内置了期望。在这种特殊情况下,如果测试实用程序未能找到h2
标记,它将抛出错误并且测试将自动失败。
现在,尝试为第二个测试创建代码。您可以使用findRenderedDOMcomponentWithClass()
来检查是否有任何具有“标题”类的节点。
it('has a title class', () => { const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>); var node = ReactTestUtils.findRenderedDOMComponentWithClass( component, 'title' ); })
就是这样!如果一切顺利,您应该会看到绿色的结果。
结论
尽管我们刚刚编写了两个测试规范,但我们在此过程中涵盖了很多内容。在下一篇文章中,我们将为我们的产品列表页面编写一些完整的测试。我们还将用 Enzyme 替换 ReactTestUtils。为什么?Enzyme 提供了一个非常易于使用且对开发人员友好的高级界面。请继续关注第二部分!
- 单元测试
- 功能测试
- 集成测试
- 有一个测试框架
- 反应测试渲染器
- ReactTestUtils 和酶
- 设置演示应用程序并组织测试
- 目录结构
- src/components/__tests__/ProductList.test.js
- src/components/ProductHeader.js
- src/components/__tests__/ProductHeader.test.js