首页  >  文章  >  web前端  >  使用 Jest 的一个示例中的单元、集成和 ETesting

使用 Jest 的一个示例中的单元、集成和 ETesting

DDD
DDD原创
2024-11-20 19:53:15238浏览

介绍

许多开发人员在测试代码时面临挑战。如果没有适当的测试,错误可能会溜走,导致用户沮丧和昂贵的修复。

本文将向您展示如何在使用 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 函数单击注册按钮。

使用不同的配置运行所有测试

Unit, Integration, and ETesting in One Example Using Jest

照片由 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中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn