使用 Jest 的一个示例中的单元、集成和 ETesting
介绍
许多开发人员在测试代码时面临挑战。如果没有适当的测试,错误可能会溜走,导致用户沮丧和昂贵的修复。
本文将向您展示如何在使用 Node.js 和 MongoDB 构建的非常简单的示例中使用 Jest、Supertest 和 Puppeteer 有效应用单元、集成和端到端测试。
读完本文,我希望您能够清楚地了解如何在自己的项目中应用这些类型的测试。
??请在此存储库中找到完整的示例。
介绍我们的依赖关系
在安装依赖项之前,让我先介绍一下我们的示例。这是一个非常简单的示例,用户可以打开注册页面,设置其注册详细信息,单击注册按钮,并将其信息存储在数据库中。
在此示例中,我们将使用以下包:
npm install --save jest express mongoose validator npm install --save-dev jest puppeteer jest-puppeteer mongodb-memory-server supertest npm-run-all
大多数依赖项都很简单,但以下是其中一些依赖项的说明:
- puppeteer:允许您控制无头浏览器 (Chrome) 进行自动化测试和网页抓取。
- Jest-Puppeteer: 它是为 Jest 预设的,集成了 Puppeteer,简化了在浏览器环境中运行端到端测试的设置。您可以将其用作 jest.config.js 文件中的预设,并且可以通过名为 jest-puppeteer.config.js 的文件自定义 Puppeteer 行为。
- mongodb-memory-server:它是一个实用程序,可启动内存中的 MongoDB 实例,以快速、独立地测试数据库交互。
- npm-run-all:用于并行或顺序运行多个 npm 脚本的 CLI 工具。
单元测试
- 定义:单元测试侧重于单独测试各个组件或功能。目标是验证每个代码单元是否按预期执行。
- 速度:单元测试通常非常快,因为它们测试小段代码而不依赖外部系统或数据库。
- 示例:在我们的用户注册示例中,单元测试可能会检查验证电子邮件地址的函数。例如,它将验证该函数是否正确地将 user@example.com 识别为有效,同时拒绝 user@.com 或 user.com。
很好,让我们将其翻译成代码。
设置单元测试环境
为了运行单元测试而没有不可预测的行为,您应该在每次测试之前重置模拟函数。您可以使用 beforeEach 挂钩来实现此目的:
// setup.unit.js beforeEach(() => { jest.resetAllMocks(); jest.restoreAllMocks(); });
测试电子邮件验证
在本例中,我们要测试 validateInput 函数:
npm install --save jest express mongoose validator npm install --save-dev jest puppeteer jest-puppeteer mongodb-memory-server supertest npm-run-all
这是一个非常简单的函数,用于验证提供的输入是否包含有效的电子邮件。这是它的单元测试:
// setup.unit.js beforeEach(() => { jest.resetAllMocks(); jest.restoreAllMocks(); });
await Expect(async () => {}).rejects:根据 Jest 文档,这是期望被拒绝的 Promise 原因的方法。
测试重复的电子邮件
让我们测试另一个函数,检查数据库中是否存在重复的电子邮件。实际上,这一点很有趣,因为我们必须处理数据库,同时单元测试不应该处理外部系统。那我们该怎么办呢?好吧,我们应该使用 Mocks。
首先,看看我们需要测试的 emailShouldNotBeDuplicated 函数:
// register.controller.js const validator = require('validator'); const registerController = async (input) => { validateInput(input); ... }; const validateInput = (input) => { const { name, email, password } = input; const isValidName = !!name && validator.isLength(name, { max: 10, min: 1 }); if (!isValidName) throw new Error('Invalid name'); ... };
如您所见,此函数向数据库发送请求以检查是否有其他用户具有相同的电子邮件地址。以下是我们如何模拟数据库调用:
// __tests__/unit/register.test.js const { registerController } = require('../controllers/register.controller'); describe('RegisterController', () => { describe('validateInput', () => { it('should throw error if email is not an email', async () => { const input = { name: 'test', email: 'test', password: '12345678' }; await expect(async () => await registerController(input)).rejects.toThrow('Invalid email'); }); }); });
我们使用 jest.spyOn(object, methodName) 模拟(监视)数据库 findOne 方法,该方法创建一个模拟函数并跟踪其调用。因此,我们可以使用 toHaveBeenNthCalledWith 来跟踪监视的 findOne 方法的调用次数和传递的参数。
集成测试
- 定义:集成测试评估多个组件如何协同工作。它检查不同功能、模块或服务之间的交互。
- 速度:集成测试比单元测试慢,因为它们涉及多个组件,并且可能需要数据库访问或网络调用。
- 示例:对于用户注册过程,集成测试可以验证发送注册请求时,用户数据是否已正确验证并存储在数据库中。此测试将确保所有组件(例如输入验证、API 端点和数据库交互)按预期协同工作。
设置集成测试环境
在编写集成测试之前,我们必须配置我们的环境:
npm install --save jest express mongoose validator npm install --save-dev jest puppeteer jest-puppeteer mongodb-memory-server supertest npm-run-all
- 如您所见,我们导出testingApp,因为我们在集成测试中需要它。我们将其导出为函数,因为它是在完全初始化之前导出的,因为 beforeAll 是异步的,module.exports 语句在 testingApp 被赋值之前运行,导致当我们尝试在测试中使用它时它是未定义的文件。
- 通过使用 Jest hooks,我们能够执行以下操作:
- beforeAll:启动 Express 服务器并连接到内存 MongoDB 数据库。
- afterAll:关闭 Express 服务器并停止正在运行的内存 MongoDB 数据库。
- beforeEach:通过在每个测试用例之前删除用户集合来清理数据库。
现在,我们已准备好运行集成测试。
测试注册 API 请求
让我们测试整个服务器端注册过程——从发送注册请求到将用户详细信息存储在数据库中并重定向到成功页面:
// setup.unit.js beforeEach(() => { jest.resetAllMocks(); jest.restoreAllMocks(); });
如您所见,registerController 函数集成了多个组件(函数),validateInput、emailShouldNotBeDuplicated 和 createUser 函数。
那么,让我们编写集成测试:
// register.controller.js const validator = require('validator'); const registerController = async (input) => { validateInput(input); ... }; const validateInput = (input) => { const { name, email, password } = input; const isValidName = !!name && validator.isLength(name, { max: 10, min: 1 }); if (!isValidName) throw new Error('Invalid name'); ... };
- 如您所见,在此测试用例中,我们使用 supertest 包向我们的 API 发送注册请求。这模拟了注册过程中的真实用户行为。
- 在本例中,我们没有模拟数据库调用,因为我们需要测试真实的行为。
端到端 (E2E) 测试
- 定义:端到端测试模拟真实的用户场景,以验证整个应用程序从开始到结束的流程。它测试整个应用程序,包括用户界面和后端服务。
- 速度:E2E 测试是三种类型中最慢的,因为它们涉及浏览应用程序界面并与各种组件交互,通常需要多个网络请求。
- 示例:在用户注册的情况下,E2E 测试将模拟用户打开注册页面,填写详细信息(如姓名、电子邮件和密码),然后单击“注册”按钮,然后检查它们是否被重定向到成功页面。此测试验证从用户的角度来看,注册过程的每个部分都可以无缝地协同工作。
让我们开始我们的示例。
搭建端到端测试环境
实际上,在我们的示例中,端到端测试的环境配置与集成测试类似。
从开始到结束的测试注册过程
在这种情况下,我们需要准确模拟真实的用户注册行为,从打开注册页面,填写详细信息(姓名、电子邮件、密码),单击“注册”按钮,最后重定向到成功页面。看一下代码:
npm install --save jest express mongoose validator npm install --save-dev jest puppeteer jest-puppeteer mongodb-memory-server supertest npm-run-all
让我们分解一下这段代码:
- 有很多工具可以用来实现端到端测试,这里我们使用 Jest 和 Puppeteer 来实现我们的端到端测试。
- 您可能想知道什么是页面变量?就像 Jest 所期望的那样,它是 Puppeteer 提供的全局变量,代表浏览器中的单个选项卡,我们可以在其中执行导航和与元素交互等操作。
- 我们使用 Puppeteer 来模拟用户行为,使用 goto 函数打开该页面,使用 type 函数填写输入,使用 click 函数单击注册按钮。
使用不同的配置运行所有测试
照片由 Nathan Dumlao 在 Unsplash 上拍摄
此时,您可能想知道当每个测试类型都有自己的配置时如何同时运行所有测试类型。例如:
- 在单元测试中,您需要在每个测试用例之前重置所有模拟,而在集成测试中,这是没有必要的。
- 在集成测试中,您必须在运行测试之前设置数据库连接,但在单元测试中,这不是必需的。
那么,我们如何同时运行所有测试类型,同时确保每个测试类型遵循其相应的配置?
要解决此问题,请按照以下步骤操作:
1. 让我们创建三个不同的配置文件,jest.unit.config.js:
// setup.unit.js beforeEach(() => { jest.resetAllMocks(); jest.restoreAllMocks(); });
jest.integration.config.js:
// register.controller.js const validator = require('validator'); const registerController = async (input) => { validateInput(input); ... }; const validateInput = (input) => { const { name, email, password } = input; const isValidName = !!name && validator.isLength(name, { max: 10, min: 1 }); if (!isValidName) throw new Error('Invalid name'); ... };
jest.e2e.config.js:
// __tests__/unit/register.test.js const { registerController } = require('../controllers/register.controller'); describe('RegisterController', () => { describe('validateInput', () => { it('should throw error if email is not an email', async () => { const input = { name: 'test', email: 'test', password: '12345678' }; await expect(async () => await registerController(input)).rejects.toThrow('Invalid email'); }); }); });
2. 接下来,更新 package.json 文件中的 npm 脚本,如下所示:
// register.controller.js const { User } = require('../models/user'); const registerController = async (input) => { ... await emailShouldNotBeDuplicated(input.email); ... }; const emailShouldNotBeDuplicated = async (email) => { const anotherUser = await User.findOne({ email }); if (anotherUser) throw new Error('Duplicated email'); };
--config:指定 Jest 配置文件的路径。
npm-run-all --parallel:允许并行运行所有测试。
3. 然后,创建三个名为 setup.unit.js、setup.integration.js 和 setup.e2e.js 的安装文件,其中包含前面部分中使用的必要安装代码。
4. 最后,通过执行此命令 npm run test 来运行所有测试。该命令将根据各自的配置并行执行所有单元、集成和端到端测试。
结论
在本文中,我们探讨了单元、集成和端到端 (E2E) 测试,强调它们对于构建可靠应用程序的重要性。我们在 Node.js 和 MongoDB 的简单用户注册示例中演示了如何使用 Jest、Supertest 和 Puppeteer 来实现这些测试方法。
事实上,可靠的测试策略不仅可以提高代码质量,还可以增强开发人员的信心并提高用户满意度。
我希望这篇文章为您提供了有用的见解,您可以将其应用到您自己的项目中。测试愉快!
想一想
如果您发现本文有用,请查看以下文章:
- MongoDB GridFS 变得简单
- 我如何使用 FFmpeg 和 Node.js 改进视频流
- 处理异步 JavaScript 的 4 种方法
非常感谢您一直陪伴我到现在。我希望您喜欢阅读这篇文章。
以上是使用 Jest 的一个示例中的单元、集成和 ETesting的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。
