利用 Jest 和Enzyme构建 TDD 应用程序

2023-11-01 08:21

本文主要是介绍利用 Jest 和Enzyme构建 TDD 应用程序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第一章 浅层渲染(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”

image-20200922112729674

当你再次更改的时候,他会返回

image-20200922113118466

image-20200922112905925

通过按 u 键,您可以更新快照来处理这个新的测试场景。

当你按下u后

image-20200922114220891

除了 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();
});

其实这里的测试有个问题

image-20200922165432918

当你调换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官网的时候

他将测试分成了一下几种

  • 创建/清理
  • act()
  • 渲染
  • 数据获取
  • mock 模块
  • 事件
  • 计时器
  • 快照测试
  • 多渲染器
  • 缺少什么?

1.创建/清理

我们已经用到过,在context的时候

常见的方法是使用一对 beforeEachafterEach 块,以便它们一直运行,并隔离测试本身造成的影响

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

这篇关于利用 Jest 和Enzyme构建 TDD 应用程序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/321728

相关文章

基于Python构建一个高效词汇表

《基于Python构建一个高效词汇表》在自然语言处理(NLP)领域,构建高效的词汇表是文本预处理的关键步骤,本文将解析一个使用Python实现的n-gram词频统计工具,感兴趣的可以了解下... 目录一、项目背景与目标1.1 技术需求1.2 核心技术栈二、核心代码解析2.1 数据处理函数2.2 数据处理流程

Python FastMCP构建MCP服务端与客户端的详细步骤

《PythonFastMCP构建MCP服务端与客户端的详细步骤》MCP(Multi-ClientProtocol)是一种用于构建可扩展服务的通信协议框架,本文将使用FastMCP搭建一个支持St... 目录简介环境准备服务端实现(server.py)客户端实现(client.py)运行效果扩展方向常见问题结

详解如何使用Python构建从数据到文档的自动化工作流

《详解如何使用Python构建从数据到文档的自动化工作流》这篇文章将通过真实工作场景拆解,为大家展示如何用Python构建自动化工作流,让工具代替人力完成这些数字苦力活,感兴趣的小伙伴可以跟随小编一起... 目录一、Excel处理:从数据搬运工到智能分析师二、PDF处理:文档工厂的智能生产线三、邮件自动化:

详解如何使用Python从零开始构建文本统计模型

《详解如何使用Python从零开始构建文本统计模型》在自然语言处理领域,词汇表构建是文本预处理的关键环节,本文通过Python代码实践,演示如何从原始文本中提取多尺度特征,并通过动态调整机制构建更精确... 目录一、项目背景与核心思想二、核心代码解析1. 数据加载与预处理2. 多尺度字符统计3. 统计结果可

一文教你Java如何快速构建项目骨架

《一文教你Java如何快速构建项目骨架》在Java项目开发过程中,构建项目骨架是一项繁琐但又基础重要的工作,Java领域有许多代码生成工具可以帮助我们快速完成这一任务,下面就跟随小编一起来了解下... 目录一、代码生成工具概述常用 Java 代码生成工具简介代码生成工具的优势二、使用 MyBATis Gen

Python使用Reflex构建现代Web应用的完全指南

《Python使用Reflex构建现代Web应用的完全指南》这篇文章为大家深入介绍了Reflex框架的设计理念,技术特性,项目结构,核心API,实际开发流程以及与其他框架的对比和部署建议,感兴趣的小伙... 目录什么是 ReFlex?为什么选择 Reflex?安装与环境配置构建你的第一个应用核心概念解析组件

Python+wxPython构建图像编辑器

《Python+wxPython构建图像编辑器》图像编辑应用是学习GUI编程和图像处理的绝佳项目,本教程中,我们将使用wxPython,一个跨平台的PythonGUI工具包,构建一个简单的... 目录引言环境设置创建主窗口加载和显示图像实现绘制工具矩形绘制箭头绘制文字绘制临时绘制处理缩放和旋转缩放旋转保存编

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St

使用Python构建一个Hexo博客发布工具

《使用Python构建一个Hexo博客发布工具》虽然Hexo的命令行工具非常强大,但对于日常的博客撰写和发布过程,我总觉得缺少一个直观的图形界面来简化操作,下面我们就来看看如何使用Python构建一个... 目录引言Hexo博客系统简介设计需求技术选择代码实现主框架界面设计核心功能实现1. 发布文章2. 加

一文详解如何从零构建Spring Boot Starter并实现整合

《一文详解如何从零构建SpringBootStarter并实现整合》SpringBoot是一个开源的Java基础框架,用于创建独立、生产级的基于Spring框架的应用程序,:本文主要介绍如何从... 目录一、Spring Boot Starter的核心价值二、Starter项目创建全流程2.1 项目初始化(