利用 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利用PySpark和Kafka实现流处理引擎构建指南

《Python利用PySpark和Kafka实现流处理引擎构建指南》本文将深入解剖基于Python的实时处理黄金组合:Kafka(分布式消息队列)与PySpark(分布式计算引擎)的化学反应,并构建一... 目录引言:数据洪流时代的生存法则第一章 Kafka:数据世界的中央神经系统消息引擎核心设计哲学高吞吐

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

Go语言使用net/http构建一个RESTful API的示例代码

《Go语言使用net/http构建一个RESTfulAPI的示例代码》Go的标准库net/http提供了构建Web服务所需的强大功能,虽然众多第三方框架(如Gin、Echo)已经封装了很多功能,但... 目录引言一、什么是 RESTful API?二、实战目标:用户信息管理 API三、代码实现1. 用户数据

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

深入浅出SpringBoot WebSocket构建实时应用全面指南

《深入浅出SpringBootWebSocket构建实时应用全面指南》WebSocket是一种在单个TCP连接上进行全双工通信的协议,这篇文章主要为大家详细介绍了SpringBoot如何集成WebS... 目录前言为什么需要 WebSocketWebSocket 是什么Spring Boot 如何简化 We

Spring Boot Maven 插件如何构建可执行 JAR 的核心配置

《SpringBootMaven插件如何构建可执行JAR的核心配置》SpringBoot核心Maven插件,用于生成可执行JAR/WAR,内置服务器简化部署,支持热部署、多环境配置及依赖管理... 目录前言一、插件的核心功能与目标1.1 插件的定位1.2 插件的 Goals(目标)1.3 插件定位1.4 核

使用Python构建一个高效的日志处理系统

《使用Python构建一个高效的日志处理系统》这篇文章主要为大家详细讲解了如何使用Python开发一个专业的日志分析工具,能够自动化处理、分析和可视化各类日志文件,大幅提升运维效率,需要的可以了解下... 目录环境准备工具功能概述完整代码实现代码深度解析1. 类设计与初始化2. 日志解析核心逻辑3. 文件处

使用Docker构建Python Flask程序的详细教程

《使用Docker构建PythonFlask程序的详细教程》在当今的软件开发领域,容器化技术正变得越来越流行,而Docker无疑是其中的佼佼者,本文我们就来聊聊如何使用Docker构建一个简单的Py... 目录引言一、准备工作二、创建 Flask 应用程序三、创建 dockerfile四、构建 Docker

基于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)运行效果扩展方向常见问题结