推荐的工具:Jest ,React 测试库。
如果你想断言或者操纵你的渲染组件,你可以使用react-testing-library, Enzyme, 或者React的 TestUtils。
react测试技巧: https://zh-hans.reactjs.org/docs/testing-recipes.html
jest-快照测试: https://jestjs.io/zh-Hans/docs/snapshot-testing
React 测试库: https://testing-library.com/docs/react-testing-library/intro/
Test Renderer: https://zh-hans.reactjs.org/docs/test-renderer.html
Test Utilities: https://reactjs.org/docs/test-utils.html#act
1 | /* # utils */ |
小结:
- 单元测试都少不了jest 做断言,mock等。
- 快照可以用enzyme 和 testing-library。
如果不关心组件实现细节,遵从用户/浏览器可以用testing-library。
enzyme 老牌了。testing-library介绍:The @testing-library family of packages helps you test UI components in a user-centric way.
enzyme 可以测组件实现细节,有3种渲染方式。而且都可以测其他框架ui/dom。
2022.8.13 星期六 :
# w3
# react文档 - API REFERENCE
1-1 Test Utilities
Test Utilities: https://reactjs.org/docs/test-utils.html#act
概览
ReactTestUtils 可搭配你所选的测试框架,轻松实现 React 组件测试。在 Facebook 内部,我们使用 Jest 来轻松实现 JavaScript 测试。你可以从 Jest 官网的 React 教程中了解如何开始使用它。
我们推荐使用 React Testing Library,它使得针对组件编写测试用例就像终端用户在使用它一样方便。
当使用的 React 版本 <= 16 时,可以使用 Enzyme 的测试工具,通过它能够轻松对 React 组件的输出进行断言、操控和遍历。
1 | import React from 'react'; |
### 参考
#### act()
为断言准备一个组件,包裹要渲染的代码并在调用 act() 时执行更新。这会使得测试更接近 React 在浏览器中的工作方式。
如果你使用了 react-test-renderer,它也提供了与 act 行为相同的函数。
### 其他工具方法
Simulate
使用可选的 eventData 事件数据来模拟在 DOM 节点上触发事件。
1 | act() |
1-2 Test Renderer
Test Renderer: https://zh-hans.reactjs.org/docs/test-renderer.html
概览
这个 package 提供了一个 React 渲染器,用于将 React 组件渲染成纯 JavaScript 对象,无需依赖 DOM 或原生移动环境。
这个 package 提供的主要功能是在不依赖浏览器或 jsdom 的情况下,返回某个时间点由 React DOM 或者 React Native 平台渲染出的视图结构(类似与 DOM 树)快照。
1-你可以使用 Jest 的快照测试功能来自动保存当前 JSON 树结构到一个文件中,并在测试中检查它是否被修改:了解更多。
2-你也可以通过遍历输出来查找特定节点,并对它们进行断言。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37import TestRenderer from 'react-test-renderer';
/* ## 1 */
function Link(props) {
return <a href={props.page}>{props.children}</a>;
}
const testRenderer = TestRenderer.create(
<Link page="https://www.facebook.com/">Facebook</Link>
);
console.log(testRenderer.toJSON());
// { type: 'a',
// props: { href: 'https://www.facebook.com/' },
// children: [ 'Facebook' ] }
/* ## 2 */
function MyComponent() {
return (
<div>
<SubComponent foo="bar" />
<p className="my">Hello</p>
</div>
)
}
function SubComponent() {
return (
<p className="sub">Sub</p>
);
}
const testRenderer = TestRenderer.create(<MyComponent />);
const testInstance = testRenderer.root;
expect(testInstance.findByType(SubComponent).props.foo).toBe('bar');
expect(testInstance.findByProps({className: "sub"}).children).toEqual(['Sub']);
参考
TestRenderer.create()
通过传来的 React 元素创建一个 TestRenderer 实例。它并不使用真实的 DOM,但是它依然将组件树完整地渲染到内存,以便于你对它进行断言。此时将返回一个 TestRenderer 实例。
TestRenderer.act()
与 react-dom/test-utils 中的 act() 相似,TestRender.act 为断言准备一个组件。可以使用 act() 来包装 TestRenderer.create 和 testRenderer.update。
1 | TestRenderer |
1-3 JavaScript 环境要求
# react文档 - 测试
0-1 测试概览
测试概览: https://zh-hans.reactjs.org/docs/testing.html
现在有许多种测试 React 组件的方法。大体上可以被分为两类:
- 渲染组件树 在一个简化的测试环境中渲染组件树并对它们的输出做断言检查。
- 运行完整应用 在一个真实的浏览器环境中运行整个应用(也被称为“端到端(end-to-end)”测试)。
推荐的工具
Jest 是一个 JavaScript 测试运行器。它允许你使用 jsdom 操作 DOM 。尽管 jsdom 只是对浏览器工作表现的一个近似模拟,对测试 React 组件来说它通常也已经够用了。Jest 有着十分优秀的迭代速度,同时还提供了若干强大的功能,比如它可以模拟 modules 和 timers 让你更精细的控制代码如何执行。
React 测试库是一组能让你不依赖 React 组件具体实现对他们进行测试的辅助工具。它让重构工作变得轻而易举,还会推动你拥抱有关无障碍的最佳实践。虽然它不能让你省略子元素来浅(shallowly)渲染一个组件,但像 Jest 这样的测试运行器可以通过 mocking 让你做到。
0-2 测试技巧
测试技巧: https://zh-hans.reactjs.org/docs/testing-recipes.html
此章节假设你正在使用 Jest 作为测试运行器。如果你使用不同的测试运行器,你可能需要调整 API,但整体的解决方案是相同的。在测试环境章节阅读更多关于设置测试环境的细节。
1 | 创建/清理 |
### 创建/清理
对于每个测试,我们通常希望将 React 树渲染给附加到 document的 DOM 元素。这点很重要,以便它可以接收 DOM 事件。当测试结束时,我们需要“清理”并从 document 中卸载树。
常见的方法是使用一对 beforeEach 和 afterEach 块,以便它们一直运行,并隔离测试本身造成的影响:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import { unmountComponentAtNode } from "react-dom";
let container = null;
beforeEach(() => {
// 创建一个 DOM 元素作为渲染目标
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// 退出时进行清理
unmountComponentAtNode(container);
container.remove();
container = null;
});
### act
act 名称来自 Arrange-Act-Assert 模式。
在编写 UI 测试时,可以将渲染、用户事件或数据获取等任务视为与用户界面交互的“单元”。react-dom/test-utils 提供了一个名为 act() 的 helper,它确保在进行任何断言之前,与这些“单元”相关的所有更新都已处理并应用于 DOM:
这有助于使测试运行更接近真实用户在使用应用程序时的体验。
你可能会发现直接使用 act() 有点过于冗长。为了避免一些样板代码,你可以使用 React 测试库,这些 helper 是使用 act() 函数进行封装的。
### 渲染
通常,你可能希望测试组件对于给定的 prop 渲染是否正确。
### 数据获取1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import User from "./user";
let container = null;
beforeEach(() => {
// 创建一个 DOM 元素作为渲染目标
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// 退出时进行清理
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("渲染用户数据", async () => {
const fakeUser = {
name: "Joni Baez",
age: "32",
address: "123, Charming Avenue"
};
jest.spyOn(global, "fetch").mockImplementation(() =>
Promise.resolve({
json: () => Promise.resolve(fakeUser)
})
);
// 使用异步的 act 应用执行成功的 promise
await act(async () => {
render(<User id="123" />, container);
});
expect(container.querySelector("summary").textContent).toBe(fakeUser.name);
expect(container.querySelector("strong").textContent).toBe(fakeUser.age);
expect(container.textContent).toContain(fakeUser.address);
// 清理 mock 以确保测试完全隔离
global.fetch.mockRestore();
});
### mock 模块
有些模块可能在测试环境中不能很好地工作,或者对测试本身不是很重要。使用虚拟数据来 mock 这些模块可以使你为代码编写测试变得更容易。
考虑一个嵌入第三方 GoogleMap 组件的 Contact 组件:
### 事件
React 测试库为触发事件提供了一个更简洁 helper。
1 | // ... // 创建/清理 |
### 计时器
### 快照测试
像 Jest 这样的框架还允许你使用 toMatchSnapshot / toMatchInlineSnapshot 保存数据的“快照”。有了这些,我们可以“保存”渲染的组件输出,并确保对它的更新作为对快照的更新显式提交。
在这个示例中,我们渲染一个组件并使用 pretty 包对渲染的 HTML 进行格式化,然后将其保存为内联快照:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25it("应渲染问候语", () => {
act(() => {
render(<Hello />, container);
});
expect(
pretty(container.innerHTML)
).toMatchInlineSnapshot(); /* ... 由 jest 自动填充 ... */
act(() => {
render(<Hello name="Jenny" />, container);
});
expect(
pretty(container.innerHTML)
).toMatchInlineSnapshot(); /* ... 由 jest 自动填充 ... */
act(() => {
render(<Hello name="Margaret" />, container);
});
expect(
pretty(container.innerHTML)
).toMatchInlineSnapshot(); /* ... 由 jest 自动填充 ... */
});
通常,进行具体的断言比使用快照更好。这类测试包括实现细节,因此很容易中断,并且团队可能对快照中断不敏感。选择性地 mock 一些子组件可以帮助减小快照的大小,并使它们在代码评审中保持可读性。
### 多渲染器
3-1 jest-快照测试
jest-教程-快照测试: https://jestjs.io/zh-Hans/docs/snapshot-testing
每当你想要确保你的UI不会有意外的改变,快照测试是非常有用的工具。
典型的做法是在渲染了UI组件之后,保存一个快照文件, 检测他是否与保存在单元测试旁的快照文件相匹配。 若两个快照不匹配,测试将失败:有可能做了意外的更改,或者UI组件已经更新到了新版本。
### Jest快照测试
测试React组件可以采用类似的方法。 你只需要测试对应的React树的序列号值即可,而不需要渲染整个React程序。
1 | import renderer from 'react-test-renderer'; |
#### 更新快照jest --updateSnapshot
或jest -u
.
如果你想限制只重新生成一部分的快照文件,你可以使用–testNamePattern 来正则匹配想要生成的快照名字。
#### 交互式快照模式
进入交互式快照模式后,Jest会让你浏览失败的快照,并让你可以看到失败用例的输出日志。
这样,你就可以选择更新快照,或者跳至下一步。
#### 内联快照
#### 属性匹配器
### 最佳实践
#### 1. 视快照如代码
#### 2. 测试应是确定性的
#### 3. 使用合理的快照描述
3-2 jest-测试React程序
jest-框架指南-测试React程序: https://jestjs.io/zh-Hans/docs/tutorial-react
在Facebook,我们使用 Jest 测试 React 应用程序。
现在,使用React的test renderer和Jest的快照特性来和组件交互,获得渲染结果和生成快照文件:
Link.test.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import renderer from 'react-test-renderer';
import Link from '../Link';
it('changes the class when hovered', () => {
const component = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>,
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
renderer.act(() => {
tree.props.onMouseEnter();
});
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
renderer.act(() => {
tree.props.onMouseLeave();
});
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
Snapshot Testing with Mocks, Enzyme and React 16+
快照测试在 Enzyme 和 React 16+ 中使用时有一个注意事项。 如果您使用以下方式模拟模块:
DOM测试
如果你想断言或者操纵你的渲染组件,你可以使用react-testing-library, Enzyme, 或者React的 TestUtils。 接下来我们讲两个 react-testing-library 和Enzyme的例子。
1 | // __tests__/CheckboxWithLabel-test.js |
### 自定义转译器
# enzyme 和 react-testing-library
enzyme
Enzyme: https://github.com/enzymejs/enzyme
Enzyme 文档: https://enzymejs.github.io/enzyme/
Time to say goodbye - Enzyme.js
最后,GitHub Insights 工具提供了有关两者使用情况的有趣统计数据。在撰写本文时,在 GitHub 上托管的开源存储库中:
354,059 个存储库依赖 Enzyme
2,440,909 个存储库依赖 React Testing Library
Testing React Hooks With Enzyme and React Testing Library
简而言之,Enzyme主要包括三个测试:
一个是浅渲染的shallow,这个生成虚DOM对象,所以渲染最快,然而它并不能测试子组件的相关代码。
另一个是DOM渲染mount,它会生成完整的DOM节点,所以可以测试子组件。但是要依赖一个用jsdom模拟的浏览器环境。
最后一个是HTML文本渲染render,它会将react组件渲染为html文本,然后在内部通过Cheerio自动生成一个Cheerio对象。
使用推荐:
shallow只渲染当前组件,只能能对当前组件做断言;
mount会渲染当前组件以及所有子组件,对所有子组件也可以做上述操作。
一般交互测试都会关心到子组件,我使用的都是mount。
但是mount耗时更长,内存啥的也都占用的更多,如果没必要操作和断言子组件,可以使用shallow。
全渲染
全渲染(full rendering)就是完整渲染出当前组件及其所有子组件,就像在真实浏览器渲染那样,当组件内部直接改变了 DOM 时,就需要使用全渲染来测试。全渲染需要真实地模拟 DOM 环境,流行的做法有以下几种:
使用 JSDOM:使用 JS 模拟 DOM 环境,能满足90%的使用场景。这是 Jest 内部所使用的全渲染框架。
使用 Cheerio:类似 JSDOM,更轻的实现,类似 jQuery 的语法。这是 Enzyme 内部使用的全渲染框架。
使用 Karma:在真实的浏览器中执行测试,也支持在多个浏览器中依次执行测试,使用的是真实DOM 环境,但速度稍慢。
组件测试框架
Jest 组件测试
Enzyme 组件测试
Enzyme 提供 3 种不同的方式来测试组件:
shallow:推荐的方式,浅渲染,只会渲染本地组件内容(只渲染不包含 children 的组件),引用的外部组件不会渲染,提供更好的隔离性。
render:如果 shallow 不能满足,才会使用它,能够渲染所有的子组件。基于 Cheerio 来模拟 DOM 环境(Cheerio 是类似 JSDOM 的另一框架)。
mount:类似 render,会做全渲染,对测试生命周期非常有用,能够访问到组件的生命周期方法,比如 componentDidUpdate 等。一般用于集成测试。
Enzyme Selector
推荐:一般组件的快照测试使用 shallow 方法即可。
推荐:如果要测试子组件,并且对组件的生命周期等方法不怎么关注,使用 render 方法。
推荐:如果要测试组件生命周期方法、子组件,使用 mount 方法。
编写组件测试
测试 rendering
测试 props
测试 events
测试 event handlers
enzyme VS react-testing-library
原文: # 71 Difference between enzyme and react-testing-library
Enzyme
Enzyme 是由 Airbnb 推出的流行的测试库。它已经发布了很长一段时间,且 react 官方文档建议减少使用 Enzyme 作为编写测试用例的模板。Enzyme 的 API 旨在通过模仿 jQuery 的 API 来实现直观和灵活的 DOM 操作和遍历。
React Testing Library
React Testing Library – 一个非常通用的名字,它作为一个测试库,旨在解决与其他测试库不同的用例。React Testing Library 迫使你编写不脆弱的测试 – 测试并不是测试具体实现,而是测试组件的功能。它鼓励编写代码的最佳实践,并使代码具备可测试性,和测试正确的条件。
更新–React Testing Library 现在改名为@testing-library/react。
让我们来看看 Enzyme 与@testing-library/react 的一些区别。
区别
#### Case 1 - 设置
在 Enzyme 中,你需要配置适配器,使其与 React 16 一起工作。还有其他的第三方适配器可以使 Enzyme 与这些库一起工作。
在 @testing-library/react 中,不需要太多的设置。你必须安装 @testing-library/react npm 模块,然后就可以了。npm install --save-dev react-testing-library
1
2
3
4mport Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";
Enzyme.configure({ adapter: new Adapter() });
#### Case 2 概念差异
当你在 Enzyme 中编写测试时,你有方法检查类的状态属性,检查传递给组件的 props 是什么。但是如果你是用道具和状态来测试组件,那就意味着你的测试很脆弱。如果明天有人改变了状态变量的名称,那么你的测试就会失败。即使组件的功能是一样的,只是因为组件中使用的状态变量名重名了,测试就会失败。由此可见单元测试的脆性。
而@testing-library/react 没有测试状态或道具的方法。相反,它测试的是 dom,也就是用户正在与之交互的东西。
@testing-library/react 的指导原则之一是
如果涉及到渲染组件,它处理的是 DOM 节点而不是组件实例,也不应该鼓励处理组件实例。
所以,你没有得到组件实例的方法,也没有自己调用组件的方法。相反,你就像用户一样在 DOM 上工作。想测试对服务器的异步函数调用吗?从 DOM 中获取按钮,点击它,模拟 API,然后在 DOM 中检查结果。
#### Case 3 没有强制更新()或强制重新渲染组件
在 Enzyme 中,你有 ForceUpdate 方法来重新渲染组件。如果你在组件内部窥探一些箭头函数(组件内部的箭头函数是错误的),那么你将不得不强制更新组件。
在@testing-library/react 中,你没有任何这样的方法。相反,它只使用 DOM 进行测试。
#### Case 4 没有浅层或深层的渲染
在@testing-library/react 中,你没有直接的方法来测试组件的实例。所以,在 React 测试库中,没有对组件进行浅层或深层的渲染。
Case 5 约束性
Enzyme 不是一个强约束(opionated)的库,它提供了访问组件内部的方法,即组件的实例方法、状态和道具。它提供了访问组件内部的方法,即组件的方法、状态和属性。但是 Enzyme 也提供了访问组件的 DOM 的方法,所以通过使用 Enzyme,你可以选择测试组件的内部结构,即组件的方法,状态和属性。
所以通过使用 Enzyme,你可以选择测试组件的内部,也可以选择不测试。Enzyme 并不强制执行任何关于你应该如何测试组件的意见。
@testing-library/react 是强约束(opionated)的库。它只提供给你渲染组件和访问 DOM 的方法,不提供访问组件的方法。它不提供访问组件内部的方法。
总结
虽然@testing-library/react 的目标是与 Enzyme 竞争,并鼓励你从用户的角度编写可测试的代码和对应测试,但它们都有用例。你不能用一个代替另一个。有时你确实需要测试组件内部的状态变化或功能,尽管从用户的角度来看,它可能没有意义。在这些情况下,需要用 Enzyme 来测试实例方法。React Testing Library 很适合测试组件的 DOM,因为它允许你像用户使用它一样进行测试。
@testing-library/react
enzyme和@testing-library/react,怎么选择呢?现在倾向于@testing-library/react,
-是因为它对react hooks支持比较好,
二是,它的测试更符合用户形为,渲染组件,查找元素,和用户使用浏览器没有什么区别。
@testing-library/react也鼓励我们,写测试要把注意力放到用户身上,测试要模拟真实用户的形为,而不是测试组件的实现细节,
如果你了解Enzyme的话,它就提供了wrapper.state方法,可以直接获取到组件的状态,wapper.instance可以直接调用组件的方法。