利用 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

相关文章

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

Jenkins构建Maven聚合工程,指定构建子模块

一、设置单独编译构建子模块 配置: 1、Root POM指向父pom.xml 2、Goals and options指定构建模块的参数: mvn -pl project1/project1-son -am clean package 单独构建project1-son项目以及它所依赖的其它项目。 说明: mvn clean package -pl 父级模块名/子模块名 -am参数

JAVA用最简单的方法来构建一个高可用的服务端,提升系统可用性

一、什么是提升系统的高可用性 JAVA服务端,顾名思义就是23体验网为用户提供服务的。停工时间,就是不能向用户提供服务的时间。高可用,就是系统具有高度可用性,尽量减少停工时间。如何用最简单的方法来搭建一个高效率可用的服务端JAVA呢? 停工的原因一般有: 服务器故障。例如服务器宕机,服务器网络出现问题,机房或者机架出现问题等;访问量急剧上升,导致服务器压力过大导致访问量急剧上升的原因;时间和

利用Django框架快速构建Web应用:从零到上线

随着互联网的发展,Web应用的需求日益增长,而Django作为一个高级的Python Web框架,以其强大的功能和灵活的架构,成为了众多开发者的选择。本文将指导你如何从零开始使用Django框架构建一个简单的Web应用,并将其部署到线上,让世界看到你的作品。 Django简介 Django是由Adrian Holovaty和Simon Willison于2005年开发的一个开源框架,旨在简

828华为云征文|华为云Flexus X实例docker部署rancher并构建k8s集群

828华为云征文|华为云Flexus X实例docker部署rancher并构建k8s集群 华为云最近正在举办828 B2B企业节,Flexus X实例的促销力度非常大,特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务的需求,一定不要错过这个机会。赶紧去看看吧! 什么是华为云Flexus X实例 华为云Flexus X实例云服务是新一代开箱即用、体