虽然拥有经过良好测试的 API 非常重要,但任何 React 应用程序都必须拥有可靠的测试覆盖率。 测试可以增强对代码的信心,并有助于防止将错误发布给用户。
因此,我们将重点关注这篇文章中的测试,特别是针对 React 应用程序。 最后,您将能够使用 Jest 和 Enzyme 运行测试。
如果您不了解这些名称,请不要担心,因为我们现在将介绍它们!
安装测试依赖项
Jest 是一个单元测试框架,它使测试 React 应用程序变得非常容易,因为它与 React 无缝协作(因为,Facebook 团队创建了它,尽管它与其他 JavaScript 框架兼容)。 它充当测试运行程序,其中包含一个完整的预定义测试库,以及模拟函数的功能。
Enzyme 旨在测试组件,它是编写断言(或场景)以模拟操作的好方法,这些操作可以确认前端 UI 是否正常工作。 换句话说,它会在前端查找组件,与它们交互,并在任何组件无法按预期工作时发出警报。
因此,Jest 和 Enzyme 是不同的工具,但它们可以很好地互补。
出于我们的目的,我们将使用 create-react-app 启动一个新的 React 项目,因为它开箱即用地配置了 Jest。
yarn create react-app my-app
我们仍然需要安装 enzyme
和 enzyme-adapter-react-16
(此数字应基于您使用的 React 版本)。
yarn add enzyme enzyme-adapter-react-16 --dev
好的,这将创建我们的项目,并在两个命令中将 Jest 和 Enzyme 都添加到我们的项目中。接下来,我们需要为我们的测试创建一个设置文件。我们将此文件命名为 setupTests.js
并将其放置在项目的 src
文件夹中。
以下是该文件的内容
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
这引入了 Enzyme 并设置了运行测试的适配器。
为了方便起见,我们将为我已经构建的 React 应用程序编写测试。 在 GitHub 上获取应用程序的副本。
拍摄测试快照
快照测试用于跟踪应用程序 UI 中的变化。如果您想知道我们是否正在处理 UI 的文字图像,答案是否定的,但快照非常有用,因为它们可以捕获组件在某一时刻的代码,以便我们可以比较组件的一种状态与它可能采用的任何其他可能状态。
测试第一次运行时,会生成组件代码的快照,并将其保存在 src
目录中的一个新的 __snapshots__
文件夹中。在测试运行时,当前 UI 会与现有 UI 进行比较。以下是示例项目的 App 组件成功测试的快照。
it("renders correctly", () => {
const wrapper = shallow(
<App />
);
expect(wrapper).toMatchSnapshot();
});
现在,运行测试
yarn run test
测试套件运行时生成的每个新快照都将保存在 __tests__
文件夹中。Jest 的优点在于它会检查组件是否匹配,然后在随后运行测试时,Jest 会检查组件是否与后续测试中的快照匹配。 此文件的外观如下所示。
让我们创建一个导致测试失败的条件。我们将组件的 <h2>
标记从 <h2>Random User</h2>
更改为 <h2>CSSTricks Tests</h2>
,以下是测试运行时在命令行中获得的结果

如果我们希望我们的更改通过测试,我们可以将标题更改回之前的状态,或者我们可以更新快照文件。Jest 甚至提供了从命令行更新快照的说明,因此无需手动更新快照
Inspect your code changes or press `u` to update them.
因此,这就是我们在此情况下要做的。我们按 u
更新快照,测试通过,然后我们继续。
您是否注意到测试快照中的 shallow
方法?它来自 Enzyme 包,并指示测试运行单个组件,而不是其他任何内容——甚至不包括可能在其中的任何子组件。这是一种隔离代码并获得更佳调试信息的好方法,尤其适用于简单、非交互式组件。
除了 shallow
之外,我们还有用于快照测试的 render
。您可能会问,有什么区别?虽然 shallow
在测试组件时会排除子组件,但 render
在呈现为静态 HTML 时会包含它们。
还有另一种需要注意的方法:mount
。这是测试中最引人注目的类型,因为它会完全呈现组件(如 shallow
和 render
)及其子组件(如 render
),但会将它们放入 DOM 中,这意味着它可以完全测试任何与 DOM API 交互的组件以及传递到和传出它的任何 props。它是交互性的全面测试。还需要注意的是,由于它进行了完全挂载,因此我们希望在测试运行后对组件调用 .unmount
,以避免与其他测试发生冲突。
测试组件的生命周期方法
生命周期方法是 React 提供的钩子,在组件生命周期的不同阶段被调用。在处理 API 调用等方面,这些方法非常方便。
由于它们经常用于 React 组件中,因此您可以让您的测试套件覆盖它们,以确保所有内容都能按预期工作。
我们在组件挂载时从 API 中获取数据。我们可以通过使用 jest 检查生命周期方法是否被调用,这使得我们能够模拟 React 应用程序中使用的生命周期方法。
it('calls componentDidMount', () => {
jest.spyOn(App.prototype, 'componentDidMount')
const wrapper = shallow(<App />)
expect(App.prototype.componentDidMount.mock.calls.length).toBe(1)
})
我们将间谍附加到组件的原型上,以及组件的 componentDidMount()
生命周期方法上的间谍。接下来,我们通过检查调用长度来断言生命周期方法被调用了一次。
测试组件 props
如何确保来自一个组件的 props 正在传递到另一个组件?当然,我们可以进行测试来确认!Enzyme API 允许我们创建一个“模拟”函数,以便测试可以模拟在组件之间传递的 props。
假设我们正在将用户 props 从主 App 组件传递到 Profile 组件。换句话说,我们希望 App 使用有关用户信息的详细信息通知 Profile,以便为该用户呈现个人资料。
首先,让我们模拟用户 props
const user = {
name: 'John Doe',
email: '[email protected]',
username: 'johndoe',
image: null
}
模拟函数看起来非常像其他测试,因为它们围绕着组件。但是,我们使用了额外的 describe
层,它接受被测试的组件,然后允许我们继续通过告诉测试我们期望传递的 props 和值。
describe ('<Profile />', () => {
it ('contains h4', () => {
const wrapper = mount(<Profile user={user} />)
const value = wrapper.find('h4').text()
expect(value).toEqual('John Doe')
})
it ('accepts user props', () => {
const wrapper = mount(<Profile user={user} />);
expect(wrapper.props().user).toEqual(user)
})
})
此特定示例包含两个测试。在第一个测试中,我们将用户 props 传递给已挂载的 Profile 组件。然后,我们检查是否可以找到与 Profile 组件中内容相对应的 <h4>
元素。
在第二个测试中,我们希望检查传递给已挂载组件的 props 是否等于我们在上面创建的模拟 props。请注意,即使我们在 Profile 组件中解构了 props,也不会影响测试。
模拟 API 调用
在我们一直在使用的项目中,有一部分是进行 API 调用以获取用户列表。猜猜看?我们也可以测试该 API 调用!
测试 API 调用的稍微棘手的地方在于,我们实际上并不想访问 API。一些 API 具有调用限制,甚至需要为进行调用付费,因此我们希望避免这种情况。值得庆幸的是,我们可以使用 Jest 模拟 axios 请求。请参阅 这篇文章,以更全面地了解如何使用 axios 进行 API 调用。
首先,我们将在与 __tests__
文件夹相同的目录中创建一个名为 __mock__
的新文件夹。当测试运行时,我们的模拟请求文件将在此处创建。
module.exports = {
get: jest.fn(() => {
return Promise.resolve({
data: [
{
id: 1,
name: 'Jane Doe',
email: '[email protected]',
username: 'jdoe'
}
]
})
})
}
我们想要检查并查看是否发出了 GET
请求。我们将为此导入 axios。
import axios from 'axios';
在导入语句下方,我们需要 Jest 将 axios 替换为我们的模拟,所以我们添加以下内容
jest.mock('axios')
Jest API 有一个 spyOn()
方法,它接受一个 accessType?
参数,可用于检查我们是否能够从 API 调用中“获取”数据。我们使用 jest.spyOn()
调用我们已在 __mock__
文件中实现的间谍方法,它可以与我们之前介绍的 shallow
、render
和 mount
测试一起使用。
it('fetches a list of users', () => {
const getSpy = jest.spyOn(axios, 'get')
const wrapper = shallow(
<App />
)
expect(getSpy).toBeCalled()
})
测试通过了!
这是一个关于在 React 应用程序中进行测试的入门介绍。希望您现在能够看到测试为项目带来的价值,以及由于 Jest 和 Enzyme 的强大功能,实现测试的相对简单性。
我在“获取测试快照”部分,没有找到关于如何实际运行测试的任何内容。
Yarn test 或 npm test
感谢您指出这一点,您可以使用
yarn run test
运行测试。我已在教程中添加了此内容。
我注意到很多 React/Enzyme 测试指南建议测试组件上是否设置了 props。这对我来说总是很奇怪,因为如果您在组件上设置了 props,那么就不应该存在这些 props 未在该组件上设置的情况。我错过了什么吗?