第一章 浅层渲染(Shallow Renderer)
当为 React 写单元测试时,浅层渲染(Shallow Renderer) 会变得十分有用。浅层渲染使你可以渲染 “单层深度” 的组件,并且对组件的 render 方法的返回值进行断言,不用担心子组件的行为,组件并没有实例化或被渲染。浅渲染并不需要 DOM。
第一节 概述
1.1 简单的使用
要开始使用 Jest,你不需要安装任何东西; 它是 Create React App 的一部分。如果查看 package.json 文件,您将看到已经有一个脚本用于运行测试。
function MyComponent() {
return (
<div>
<span className="heading">Title</span>
<Subcomponent foo="bar" />
</div>
);
}
import ShallowRenderer from 'react-test-renderer/shallow';
// in your test:
const renderer = new ShallowRenderer();
renderer.render(<MyComponent />);
const result = renderer.getRenderOutput();
expect(result.type).toBe('div');
expect(result.props.children).toEqual([
<span className="heading">Title</span>,
<Subcomponent foo="bar" />
]);
1.2 API讲解
shallowRenderer.render()shallowRenderer.render() 和 ReactDOM.render()很像。但是它不需要 DOM 并且只渲染一层。这就意味着你可以测试与子组件行为隔离的组件。
shallowRenderer.getRenderOutput()在 shallowRenderer.render() 被调用后, 你可以调用 shallowRenderer.getRenderOutput() 来获取浅渲染的输出.
expect在编写测试时,通常需要检查值是否满足某些条件。Expect 可以让你访问许多“匹配器” ,让你验证不同的东西。
.toBe(value)使用。比较基本值或检查对象实例的引用标识。它调用 Object.is 来比较值,这在测试中甚至比 = = = 严格相等运算符更好。
当我们在jest中进行年使用的时候
const can = {
name: 'pamplemousse',
ounces: 12,
};
describe('the can', () => {
test('has 12 ounces', () => {
expect(can.ounces).toBe(12);
});
test('has a sophisticated name', () => {
expect(can.name).toBe('pamplemousse');
});
});
.toMatchSnapshot(propertyMatchers?, hint?)这可以确保某个值与最近的快照匹配。有关详细信息,请参阅快照测试指南。
使用 Jest toMatchSnapshot 假设,您可以测试组件的结构。将呈现组件,而 toMatchSnapshot 将从这个呈现中创建一个快照,并在每次测试运行时将其与实际组件进行比较:
1.3 向 SubHeader 组件传值
您可以通过向 SubHeader 组件传递(例如) title prop 来检查快照的工作方式。要做到这一点,创建一个新的测试场景,它应该呈现带有标题的 SubHeader。另外,将渲染器常量的创建移动到 describe 函数,这样它就可以被所有的测试场景使用:
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import SubHeader from './SubHeader';
describe('the <SubHeader /> component', () => {
+ const renderer = new ShallowRenderer();
it('should render', () => {
- const renderer = new ShallowRenderer();
renderer.render(<SubHeader />);
const component = renderer.getRenderOutput();
expect(component).toMatchSnapshot();
});
+ it('should render with a dynamic title', () => {
+ renderer.render(<SubHeader title='Test Application' />);
+ const component = renderer.getRenderOutput();
+ expect(component).toMatchSnapshot();
+ });
});
这里我们看到他传递了title = “Test Application”
当你再次更改的时候,他会返回
通过按 u 键,您可以更新快照来处理这个新的测试场景。
当你按下u后
除了 title 之外,这个组件使用 goBack 和 openForm 作为道具,其中 openForm 道具的默认值为 false。
当 goBack 有一个值时,创建一个按钮将我们带回到前一页,而当 openForm 有一个值时,创建一个按钮允许我们进入下一页,这样我们就可以添加一个新的评论。您还需要将这两个新的测试场景添加到 src/components/Header/SubHeader.test.js 文件中:
您现在已经为 SubHeader 组件创建了两个以上的快照,这将导致总共四个快照。Jest 做的另一件事是向您展示测试覆盖了多少行代码。您的测试覆盖率越高,就越有理由认为您的代码是稳定的。你可以通过使用 – coverage 标志执行测试脚本命令来检查你的代码的测试覆盖率,或者在你的终端中使用以下命令:
在创建后面两个快照的时候,会导致一些的错误
使用
npm run test -- --coverage
来查看覆盖率
第二章 使用断言来测试组件
快照测试并不一定是不好的实践; 但是,随着时间的推移,您的文件可能会变得相当大
使用断言来测试
你可以删除快照测试,因为你只想用断言来测试孩子:
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import Button from './Button';
describe('the <Button /> component', () => {
const renderer = new ShallowRenderer();
it('should render the correct children', () => {
const children = 'This is a button';
renderer.render(<Button>{children}</Button>);
const component = renderer.getRenderOutput();
expect(component.props.children).toEqual(children);
});
});
断言就是expect
第三章 使用Enzyme进行浅层渲染
npm install enzyme enzyme-adapter-react-16 --save-dev
Enzyme 和浅层渲染的区别
- renderer.render(<SubHeader title='Test Application' />);
- const component = renderer.getRenderOutput();
const component = shallow(<SubHeader title='Test Application' />);
- renderer.render(<SubHeader goBack={() => {}} />);
- const component = renderer.getRenderOutput();
const component = shallow(<SubHeader goBack={() => {}} />);
- expect(component.props.children).toEqual(children)
+ expect(component.props().children).toEqual(children)
第四章 测试shallow rendering呈现的断言
一个简单的style的教程
https://www.jianshu.com/p/2d5f037c7df9
第一节 模拟点击事件
.simulate(event[, data]) => ShallowWrapper
Simulates an event on the current node.
expect(mockOnClick).toHaveBeenCalled();
使用.toHaveBeenCalled可以确保调用了模拟函数。
这样子 ,我们在button组件中设置好了,如何在
subHeader中使用
1.检查标题
it('should render', ()=>{
const component = shallow(<SubHeader />);
expect(component).toMatchSnapshot();
})
2.检查goBackButton 是不是存在
需要导入SubHeaderButton
it('should render with a goback button', () => {
const mockGoBack = jest.fn();
const component = shallow(<SubHeader goBack={mockGoBack} />);
const goBackButton = component.find(SubHeaderButton);
expect(goBackButton.exists()).toBe(true);
goBackButton.simulate('click');
expect(mockGoBack).toHaveBeenCalled();
});
SubHeaderButton 是使用style的,所以,你要先了解下
find(selector) => ShallowWrapper
Find every node in the render tree that matches the provided selector.
.at(index) => ShallowWrapper
Returns a wrapper of the node at the provided index of the current wrapper.
完整的
it('should render with a buttons and handle the onClick events', () => {
const mockGoBack = jest.fn();
const mockOpenForm = jest.fn();
const component = shallow(<SubHeader openForm={mockOpenForm} goBack={mockGoBack} />);
const goBackButton = component.find(SubHeaderButton).at(0);
expect(goBackButton.exists()).toBe(true);
const openFormButton = component.find(SubHeaderButton).at(1);
expect(openFormButton.exists()).toBe(true)
goBackButton.simulate('click');
expect(mockGoBack).toHaveBeenCalled();
openFormButton.simulate('click');
expect(mockOpenForm).toHaveBeenCalled();
});
其实这里的测试有个问题
当你调换openForm何goBack两者的位置,它没有因为洽谈而区别,因为他们有这一样的测试特征
第五章 与Enzyme的集成测试
第一节 测试Context
测试场景检查 Hotels 组件在第一次挂载时是否会从 Context 调用 getHotelsRequest 函数。这意味着在酒店使用的 useEffect Hook 已经被测试过了。
这里测试
import React from 'react';
import { mount } from 'enzyme';
import Hotels from './Hotels';
let useContextMock;
beforeEach(() => {
useContextMock = React.useContext = jest.fn();
});
afterEach(() => {
useContextMock.mockReset();
});
describe('the <Hotels /> component', () => {
it('should handle the first mount', () => {
const mockContext = {
loading: true,
error: '',
hotels: [],
getHotelsRequest: jest.fn(),
}
useContextMock.mockReturnValue(mockContext);
const wrapper = mount(<Hotels />);
expect(mockContext.getHotelsRequest).toHaveBeenCalled();
});
});
第二节 测试alert 加载数据
因为数据仍然在这里加载,所以我们还可以测试 Alert 组件是否正在呈现来自 Context 的加载值并显示加载消息。
- const Alert = styled.span`
+ export const Alert = styled.span`
- import Hotels from './Hotels';
+ import Hotels, { Alert } from './Hotels';
+ expect(wrapper.find(Alert).text()).toBe('Loading...');
在挂载了 Hotels 组件并获取了数据之后,上下文中的加载、错误和 hotel 的值将被更新。当加载和错误的值为 false 时,HotelItemsWrapper 组件将由 Hotels 呈现。
- const HotelItemsWrapper = styled.div`
+ export const HotelItemsWrapper = styled.div`
- import Hotels, { Alert } from './Hotels';
+ import Hotels, { Alert, HotelItemsWrapper } from './Hotels';
+ it('should render the list of hotels', () => {
+ const mockContext = {
+ loading: false,
+ error: '',
+ hotels: [{
+ id: 123,
+ title: 'Test Hotel',
+ thumbnail: 'test.jpg',
+ }],
+ getHotelsRequest: jest.fn(),
+ }
+ useContextMock.mockReturnValue(mockContext);
+ const wrapper = mount(<Hotels />);
+ expect(wrapper.find(HotelItemsWrapper).exists()).toBe(true);
+ });
会不会加载消息,使用text()
同时在理,我们还需要解决ROuter 的问题
你不应该在一个 < router > 外面使用 < Link > ,因为 Enzyme 不能呈现 Link 组件
+ import { BrowserRouter as Router } from 'react-router-dom';
- const wrapper = mount(<Hotels />);
+ const wrapper = mount(<Router><Hotels /></Router>);
第三节 Context 的酒店数据
在 HotelItemsWrapper 组件内部有一个 map 函数,该函数迭代来自 Context 的酒店数据。
- const Title = styled.h3`
+ export const Title = styled.h3
+ import HotelItem, { Title } from './HotelItem';
+ expect(wrapper.find(HotelItem).exists()).toBe(true);
+ expect(wrapper.find(HotelItem).at(0).find(Title).text()).toBe(mockContext.hotels[0].title);
最后运行
npm run test --coverage!!
第六章 总结
当我们打开React官网的时候
他将测试分成了一下几种
1.创建/清理
我们已经用到过,在context的时候
常见的方法是使用一对 beforeEach
和 afterEach
块,以便它们一直运行,并隔离测试本身造成的影响
2.act()
在编写 UI 测试时,可以将渲染、用户事件或数据获取等任务视为与用户界面交互的“单元”。react-dom/test-utils
提供了一个名为 act()
的 helper,它确保在进行任何断言之前,与这些“单元”相关的所有更新都已处理并应用于 DOM:
3.渲染
通常,你可能希望测试组件对于给定的 prop 渲染是否正确。此时应考虑实现基于 prop 渲染消
4.数据获取
你可以使用假数据来 mock 请求,而不是在所有测试中调用真正的 API。使用“假”数据 mock 数据获取可以防止由于后端不可用而导致的测试不稳定,并使它们运行得更快。注意:你可能仍然希望使用一个“端到端”的框架来运行测试子集,该框架可显示整个应用程序是否一起工作。
我们也已经用到过了
5.mock 模块
6.Events
我们建议在 DOM 元素上触发真正的 DOM 事件,然后对结果进行断言。
7.计时器
你的代码可能会使用基于计时器的函数(如 setTimeout
)来安排将来更多的工作。
8.快照测试
像 Jest 这样的框架还允许你使用 toMatchSnapshot
/ toMatchInlineSnapshot
保存数据的“快照”。有了这些,我们可以“保存”渲染的组件输出,并确保对它的更新作为对快照的更新显式提交。
9.多渲染器
在极少数情况下,你可能正在使用多个渲染器的组件上运行测试。例如,你可能正在使用 react-test-renderer
组件上运行快照测试,该组件内部使用子组件内部的 ReactDOM.render
渲染一些内容。在这个场景中,你可以使用与它们的渲染器相对应的 act()
来包装更新。
显然,使用enzyme ,并遵行这些测试.
附录 参考
https://github.com/PacktPublishing/React-Projects/tree/master/Chapter06
https://www.packtpub.com/product/react-projects/9781789954937
接下去,我需要好好了解下,
进阶
https://www.packtpub.com/product/mastering-react-test-driven-development/9781789133417