使用 Jest 覆盖各个测试中的函数

使用 jest 覆盖各个测试中的函数

有时您想在某些测试中模拟某个函数,但不想在其他测试中模拟。有时您想为不同的测试提供不同的模拟。 jest 使这变得棘手:它的默认行为是覆盖整个测试文件的包函数,而不仅仅是单个测试。如果您使用过 python 的 @patch 或 laravel 的服务容器等灵活工具,这似乎很奇怪。

这篇文章将向您展示如何模拟单个测试的函数,然后在未提供模拟的情况下回退到原始实现。将给出 commonjs 和 es 模块的示例。本文中演示的技术适用于第一方模块和第三方包。

commonjs 与 es 模块

由于我们将在这篇文章中介绍多个模块系统,因此了解它们是什么很重要。

commonjs(简称cjs)是node.js中的模块系统。它使用 module.exports 导出函数并使用 require() 导入函数:

// commonjs export function greet() {  return "hello, world!";}module.exports = { greet };
// commonjs importconst getuserslist = require('./greet');

es 模块(缩写为 esm)是浏览器使用的模块系统。它使用export关键字导出函数并使用import关键字导入函数:

// es module exportexport default function greet() {  return "hello, world!";}
// es module importimport { greet } from "./greet";

在撰写本文时,大多数前端 javascript 开发人员都使用 es 模块,许多服务器端 js 开发人员也使用它们。然而,commonjs 仍然是 node 的默认设置。无论您使用哪个系统,都值得阅读整篇文章来了解 jest 的模拟系统。

使用 commonjs 模拟单个导出函数

通常,commonjs 文件将使用对象语法导出其模块,如下所示:

// commonjs export function greet() {  return "hello, world!";}module.exports = { greet: greet };

但是,也可以自行导出函数:

// commonjs export function greet() {  return "hello, world!";}module.exports = greet;

我不一定建议在您自己的代码中执行此操作:导出对象会让您在开发应用程序时减少一些麻烦。然而,这种情况很常见,值得讨论如何在 commonjs 中模拟裸导出的函数,然后在测试未提供自己的实现时回退到原始函数。

假设我们有以下 commonjs 文件,我们想在测试期间模拟:

// cjsfunction.jsfunction testfunc() {  return "original";}module.exports = testfunc;

我们可以使用以下代码在测试中模拟它:

const testfunc = require("./cjsfunction");jest.mock("./cjsfunction");beforeeach(() => {  testfunc.mockimplementation(jest.requireactual("./cjsfunction"));});it("can override the implementation for a single test", () => {  testfunc.mockimplementation(() => "mock implementation");  expect(testfunc()).tobe("mock implementation");  expect(testfunc.mock.calls).tohavelength(1);});it("can override the return value for a single test", () => {  testfunc.mockreturnvalue("mock return value");  expect(testfunc()).tobe("mock return value");  expect(testfunc.mock.calls).tohavelength(1);});it("returns the original implementation when no overrides exist", () => {  expect(testfunc()).tobe("original");  expect(testfunc.mock.calls).tohavelength(1);});

怎么运行的

当我们调用 jest.mock(“./cjsfunction”) 时,这会用自动模拟(文档)替换模块(文件及其所有导出)。当调用自动模拟时,它将返回未定义。但是,它将提供用于覆盖模拟的实现、返回值等的方法。你可以在 jest mock functions 文档中看到它提供的所有属性和方法。

我们可以使用mock的mockimplementation()方法自动将mock的实现设置为原始模块的实现。 jest 提供了一个 jest.requireactual() 方法,该方法将始终加载原始模块,即使它当前正在被模拟。

mock 实现和返回值在每次测试后都会自动清除,因此我们可以将回调函数传递给 jest 的 beforeeach() 函数,该函数在每次测试前将模拟的实现设置为原始实现。然后,任何希望提供自己的返回值或实现的测试都可以在测试主体中手动执行此操作。

导出对象时模拟 commonjs

假设上面的代码导出了一个对象而不是单个函数:

青柚面试 青柚面试

简单好用的日语面试辅助工具

青柚面试 57 查看详情 青柚面试

// cjsmodule.jsfunction testfunc() {  return "original";}module.exports = {  testfunc: testfunc,};

我们的测试将如下所示:

const cjsmodule = require("./cjsmodule");aftereach(() => {  jest.restoreallmocks();});it("can override the implementation for a single test", () => {  jest    .spyon(cjsmodule, "testfunc")    .mockimplementation(() => "mock implementation");  expect(cjsmodule.testfunc()).tobe("mock implementation");  expect(cjsmodule.testfunc.mock.calls).tohavelength(1);});it("can override the return value for a single test", () => {  jest.spyon(cjsmodule, "testfunc").mockreturnvalue("mock return value");  expect(cjsmodule.testfunc()).tobe("mock return value");  expect(cjsmodule.testfunc.mock.calls).tohavelength(1);});it("returns the original implementation when no overrides exist", () => {  expect(cjsmodule.testfunc()).tobe("original");});it("can spy on calls while keeping the original implementation", () => {  jest.spyon(cjsmodule, "testfunc");  expect(cjsmodule.testfunc()).tobe("original");  expect(cjsmodule.testfunc.mock.calls).tohavelength(1);});

怎么运行的

jest.spyon() 方法允许 jest 记录对对象方法的调用并提供自己的替换。这适用于对象,我们可以使用它,因为我们的模块正在导出包含我们的函数的对象。

spyon() 方法是一个模拟方法,因此必须重置其状态。 jest spyon() 文档建议在 aftereach() 回调中使用 jest.restoreallmocks() 重置状态,这就是我们上面所做的。如果我们不这样做,则在调用spyon()后,模拟将在下一次测试中返回未定义。

模拟es模块

es 模块可以有默认的和命名的导出:

// esmmodule.jsexport default function () {  return "original default";}export function named() {  return "original named";}

上面文件的测试如下所示:

import * as esmmodule from "./esmmodule";aftereach(() => {  jest.restoreallmocks();});it("can override the implementation for a single test", () => {  jest    .spyon(esmmodule, "default")    .mockimplementation(() => "mock implementation default");  jest    .spyon(esmmodule, "named")    .mockimplementation(() => "mock implementation named");  expect(esmmodule.default()).tobe("mock implementation default");  expect(esmmodule.named()).tobe("mock implementation named");  expect(esmmodule.default.mock.calls).tohavelength(1);  expect(esmmodule.named.mock.calls).tohavelength(1);});it("can override the return value for a single test", () => {  jest.spyon(esmmodule, "default").mockreturnvalue("mock return value default");  jest.spyon(esmmodule, "named").mockreturnvalue("mock return value named");  expect(esmmodule.default()).tobe("mock return value default");  expect(esmmodule.named()).tobe("mock return value named");  expect(esmmodule.default.mock.calls).tohavelength(1);  expect(esmmodule.named.mock.calls).tohavelength(1);});it("returns the original implementation when no overrides exist", () => {  expect(esmmodule.default()).tobe("original default");  expect(esmmodule.named()).tobe("original named");});

怎么运行的

这看起来几乎与前面的 commonjs 示例相同,但有几个关键区别。

首先,我们将模块作为命名空间导入。

import * as esmmodule from "./esmmodule";

然后当我们想要监视默认导出时,我们使用“default”:

  jest    .spyon(esmmodule, "default")    .mockimplementation(() => "mock implementation default");

es 模块导入故障排除

有时尝试使用第三方包调用 jest.spyon() 时,你会收到如下错误:

    typeerror: cannot redefine property: usenavigate        at function.defineproperty ()

当您遇到此错误时,您需要模拟导致问题的包:

import * as reactrouterdom from "react-router-dom";// add this:jest.mock("react-router-dom", () => {  const originalmodule = jest.requireactual("react-router-dom");  return {    __esmodule: true,    ...originalmodule,  };});aftereach(() => {  jest.restoreallmocks();});

此代码将模块替换为 jest es 模块模拟,其中包含使用 jest.mocks 的工厂参数的模块的所有原始属性。每当在 jest.mock 中使用工厂参数来模拟 es 模块时,都需要 __esmodule 属性(文档)。

如果需要,您还可以替换工厂参数中的单个函数。例如,如果消费者在 router 上下文之外调用 usenavigate(),react router 将抛出错误,因此如果需要,我们可以使用 jest.mock() 在整个测试文件中替换该函数:

jest.mock("react-router-dom", () => {  const originalModule = jest.requireActual("react-router-dom");  return {    __esModule: true,    ...originalModule,    // Dummy that does nothing.    useNavigate() {      return function navigate(_location) {        return;      };    },  };});

包起来

我希望这些信息在您编写自己的测试时很有价值。当测试本身未提供实现时,并非每个应用程序都会受益于能够回退到默认实现。事实上,许多应用程序希望对整个测试文件使用相同的模拟。然而,这篇文章中展示的技术将使您能够对模拟进行细粒度的控制。

如果我遗漏了某些内容,或者是否有我没有包含在这篇文章中的内容,请告诉我。

以上就是使用 Jest 覆盖各个测试中的函数的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/466280.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月8日 05:31:06
下一篇 2025年11月8日 05:32:17

相关推荐

  • C++如何实现简单任务提醒程序

    答案:C++凭借其性能控制、静态类型安全和原生可执行特性,适合开发高效、可靠的任务提醒程序。通过定义Task结构体管理任务数据,利用文件I/O实现数据持久化,并使用std::chrono处理时间比较,程序能在每次运行时检查即将或已逾期任务,结合命令行交互提供基础但完整的提醒功能。 实现一个简单的C+…

    2025年12月18日
    000
  • C++异常边界处理 模块间异常传递

    在C++跨模块调用中,必须在接口层通过try-catch阻止异常穿透边界,将C++异常转换为错误码或错误信息,如通过返回值和get_last_error()机制传递,确保调用方安全获取错误详情,避免因编译环境不一致导致未定义行为。 在C++项目中,尤其是大型系统或模块化设计中,异常的跨模块传递是一个…

    2025年12月18日
    000
  • VS Code配置C++环境需要安装哪些必要的插件

    最核心且不可或缺的插件是微软官方的C/C++扩展,它提供智能感知、代码导航、调试支持和语法格式化,是VS Code进行C++开发的基础。在此基础上,若使用CMake构建项目,CMake Tools能自动配置、构建和调试;而Code Runner则适合快速运行单文件测试。此外,Better Comme…

    2025年12月18日
    000
  • C++物联网网关 设备数据采集转发

    C++物联网网关凭借高性能、低资源消耗和底层硬件控制能力,在设备数据采集与转发中优势显著。它通过支持多协议接入(如Modbus、MQTT)、实现数据过滤聚合与边缘计算,并利用异步I/O和插件化架构提升并发与扩展性,同时结合TLS加密、缓存重传机制保障安全可靠传输。模块化设计、配置驱动与消息队列进一步…

    2025年12月18日
    000
  • 如何在C++项目中集成第三方库 比如Boost或OpenCV

    c++kquote>集成第三方库需配置头文件路径、库文件路径及链接库,CMake通过find_package等命令简化跨平台集成,避免手动配置的路径错误、版本不匹配、ABI不兼容和运行时依赖缺失等问题,是处理Boost、OpenCV等大型库依赖管理的最佳实践。 在C++项目中集成第三方库,比如…

    2025年12月18日
    000
  • C++隐私计算环境 Intel SGX开发套件安装

    答案是搭建C++隐私计算环境需安装Intel SGX开发套件,核心步骤包括:启用BIOS中SGX选项并分配内存,安装Ubuntu LTS系统及依赖包,编译安装SGX驱动、SDK和PSW,配置环境变量后验证示例程序。 C++隐私计算环境下的Intel SGX开发套件安装,核心在于搭建一个能够支持安全飞…

    2025年12月18日
    000
  • C++结构体大小端 字节序敏感数据处理

    C++结构体跨平台通信时需处理字节序差异,核心是统一数据协议并进行字节序转换。不同系统(如小端x86与大端网络字节序)对多字节数据存储顺序不同,直接传输会导致解析错误。解决方法包括:1. 明确数据交换格式,通常采用大端(网络字节序);2. 使用htonl/ntohl等函数在发送前转换、接收后还原;3…

    2025年12月18日
    000
  • Linux环境下使用Vim搭建一个轻量级的C++ IDE

    将Vim打造成C++ IDE需配置插件与工具链,核心是vim-plug管理插件,安装YouCompleteMe实现智能补全,配合ALE进行语法检查,NERDTree导航文件,Tagbar浏览符号,UltiSnips管理代码片段,并通过.ycm_extra_conf.py或compile_comman…

    2025年12月18日
    000
  • C++ Windows环境搭建 Visual Studio安装配置

    最直接的C++开发环境搭建方式是安装Visual Studio,首选社区版并勾选“使用C++的桌面开发”工作负载,确保安装MSVC编译器、Windows SDK及CMake工具(如需),避免勾选无关组件以减少臃肿;首次使用时常见问题包括项目类型选择错误、缺少头文件或库、编码乱码等,需通过项目属性配置…

    2025年12月18日
    000
  • C++结构化绑定进阶 多返回值处理

    结构化绑定通过auto [var1, var2, …] = func();语法,直接解包pair、tuple或聚合类型,使多返回值处理更清晰;它提升代码可读性,简化错误处理与自定义类型协同,支持从标准库到私有封装类的灵活应用,显著优化函数调用表达力与维护性。 C++的结构化绑定(Stru…

    2025年12月18日
    000
  • C++机器学习入门 线性回归实现示例

    首先实现线性回归模型,通过梯度下降最小化均方误差,代码包含数据准备、训练和预测,最终参数接近真实关系,适用于高性能场景。 想用C++实现线性回归,其实并不复杂。虽然Python在机器学习领域更常见,但C++凭借其高性能,在对效率要求高的场景中非常适用。下面是一个简单的线性回归实现示例,帮助你入门C+…

    2025年12月18日
    000
  • 如果C++程序忘记delete new出来的内存会发生什么

    内存泄漏指程序未释放不再使用的内存,导致内存占用持续增长,最终引发性能下降或崩溃。C++不自动回收内存是为了避免垃圾回收机制带来的性能开销,赋予程序员更高控制权。解决内存泄漏的核心是遵循RAII原则,优先使用智能指针(如std::unique_ptr、std::shared_ptr)管理资源,结合现…

    2025年12月18日
    000
  • 不使用IDE如何用命令行编译和运行一个C++程序

    答案是使用命令行编译和运行C++程序需调用编译器(如g++)将源码编译为可执行文件并运行,例如g++ hello.cpp -o hello生成可执行文件,./hello运行程序;对于多文件项目需包含所有.cpp文件,使用-I指定头文件路径,-L和-l链接库;通过Makefile或CMake自动化管理…

    2025年12月18日
    000
  • C++CSV文件处理 逗号分隔数据读写

    C++处理CSV文件需解析和生成逗号分隔的文本,核心挑战在于应对不规范格式和特殊字符。基础方法使用std::ifstream和std::ofstream结合std::stringstream进行读写,但对含逗号、换行符或双引号的字段处理不足。为高效读取大文件,可采用缓冲读取、减少字符串拷贝(如用st…

    2025年12月18日 好文分享
    000
  • C++量子计算环境 Qiskit库配置方法

    要配置Qiskit库用于C++环境,需通过pybind11创建Python与C++的绑定,使C++程序能调用Qiskit的量子计算功能。首先安装Python、Qiskit和pybind11,然后编写封装Qiskit逻辑的Python模块(如qiskit_logic.py),再用pybind11编写C…

    2025年12月18日
    000
  • C++17结构化绑定 多返回值解包技巧

    结构化绑定通过auto [var1, var2, …] = expression;语法,直接将复合类型(如pair、tuple、结构体、数组、map)的元素解包为具名变量,提升代码可读性与简洁性。它解决了传统访问方式(如.first、.second或std::get())语义不清、冗长易…

    2025年12月18日
    000
  • C++结构体C语言兼容 跨语言交互设计

    C++结构体实现C语言兼容需遵循C内存布局规则,核心是使用POD类型、extern “C”链接、控制内存对齐,并避免虚函数、非POD成员等破坏兼容性的特性,以确保跨语言交互。 C++结构体要实现C语言兼容性,核心在于遵循C语言的数据布局规则,主要通过使用POD(Plain O…

    2025年12月18日
    000
  • 怎样搭建C++计算机视觉环境 OpenCV安装指南

    答案是准备Visual Studio、CMake、OpenCV源码及contrib模块,使用CMake配置并编译,最后在VS中配置包含目录、库目录和依赖项。 搭建C++计算机视觉环境,特别是集成OpenCV,核心在于正确处理依赖、编译库文件,并在开发环境中配置好路径。这听起来可能有点技术性,但实际上…

    2025年12月18日
    000
  • C++代码格式化 Clang-Format配置指南

    统一C++代码格式规范能提升团队协作效率、降低维护成本,Clang-Format通过.clang-format配置文件实现自动化格式化,确保代码风格一致,减少无谓争论,并可通过集成到CI/CD流程中强制执行,保障代码质量。 C++代码格式化,特别是通过Clang-Format来实现,其核心目的在于建…

    2025年12月18日
    000
  • 怎样搭建C++的云函数开发环境 AWS Lambda C++运行时配置

    要在 aws lambda 上用 c++++ 写云函数,第一步是搭建开发环境。1. 安装 c++ 编译器(如 g++ 或 clang++);2. 安装并配置 aws cli;3. 了解 lambda 执行模型;4. 使用 amazon linux 环境或 docker 模拟编译环境以避免依赖问题;5…

    2025年12月18日 好文分享
    000

发表回复

登录后才能评论
关注微信