所属分类:web前端开发
测试代码对于许多开发人员来说是一种令人困惑的做法。这是可以理解的,因为编写测试需要更多的精力、时间和预见可能用例的能力。由于缺乏资源和人力,从事小型项目的初创公司和开发人员通常倾向于完全忽略测试。
但是,我认为您应该测试您的组件有几个原因:
React 也没有什么不同。当您的整个应用程序开始变成一堆难以维护的组件时,测试可以提供稳定性和一致性。从第一天开始编写测试将帮助您编写更好的代码、轻松发现错误并维护更好的开发工作流程。
在本文中,我将带您了解为 React 组件编写测试所需了解的所有内容。我还将介绍一些最佳实践和技术。让我们开始吧!
测试是验证我们的测试断言是否正确以及它们在应用程序的整个生命周期中保持正确的过程。测试断言是一个布尔表达式,除非代码中存在错误,否则它会返回 true。
例如,断言可以像这样简单:“当用户导航到 /login 时,应该呈现 id 为 #login
的模式。 ”因此,如果事实证明您以某种方式弄乱了登录组件,则断言将返回 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
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
该演示非常适合单元测试和功能测试。您可以单独测试每个组件和/或测试整个产品列表功能。
下载演示后,在 /src/components/ 内创建一个名为 __tests__ 的目录强>。然后,您可以将与此功能相关的所有测试文件存储在 __tests__ 目录中。测试人员通常将其测试文件命名为 .spec.js 或 .test.js,例如,ProductHeader.test.js 或 >ProductHeader.spec.js。
如果尚未创建 ProductHeader.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 中可用的热门匹配器列表。
toBe();
toBeNull()
toBeDefined()
toBeUndefine()
toBeTruthy()
toBeFalsy()
toBeGreaterThan()
toBeLesserThan()
toMatch()
toContain()
这只是一种味道。您可以在参考文档中找到所有可用的匹配器。
首先,我们将为 ProductHeader
组件编写一些测试。打开 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。删除虚拟规格并添加以下代码:
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
导出的一些 API 来完成此操作。例如,要渲染我们的
组件,您可以执行以下操作:
const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>);
然后,您可以借助 findRenderedDOMComponentWithTag('tag-name')
从组件中提取 h2
标签。它检查所有子节点并找到与 tag-name
匹配的节点。
这是整个测试规范。
it('has an h2 tag', () => { const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>); var h2 = ReactTestUtils.findRenderedDOMComponentWithTag( component, 'h2' ); });
尝试保存它,您的测试运行程序应该向您显示测试已通过。这有点令人惊讶,因为我们没有像前面的示例中那样的 expect()
语句。 ReactTestUtils 导出的大多数方法都内置了期望。在这种特殊情况下,如果测试实用程序无法找到 h2
标记,它将抛出错误并且测试将自动失败。
现在,尝试为第二个测试创建代码。您可以使用 findRenderedDOMcomponentWithClass()
来检查是否有任何带有“title”类的节点。
it('has a title class', () => { const component = ReactTestUtils.renderIntoDocument(<ProductHeader/>); var node = ReactTestUtils.findRenderedDOMComponentWithClass( component, 'title' ); })
就是这样!如果一切顺利,您应该会看到绿色的结果。
虽然我们刚刚编写了两个测试规范,但我们已经在此过程中涵盖了很多基础知识。在下一篇文章中,我们将为我们的产品列表页面编写一些完整的测试。我们还将用 Enzyme 替换 ReactTestUtils。为什么? Enzyme 提供了一个非常易于使用且对开发人员友好的高级界面。请继续关注第二部分!
如果您在任何时候感到困难或需要帮助,请在评论中告诉我们。