如何使用 Jest 模拟封装在服务类中的 Winston 记录器实例

2023-11-24

我想嘲笑温斯顿记录器封装在使用 NestJS 创建的服务类中的实例。我在下面包含了我的代码。

我无法从服务类中触发模拟的记录器实例。谁能解释我哪里出了问题?

import * as winston from 'winston';

import { loggerOptions } from '../logger/logger.config';
import { LoggingService } from '../logger/logger.service';

const logger: winston.Logger = winston.createLogger(loggerOptions);

// trying to mock createLogger to return a specific logger instance
const winstonMock = jest.mock('winston', () => (
    {
        format: {
            colorize: jest.fn(),
            combine: jest.fn(),
            label: jest.fn(),
            timestamp: jest.fn(),
            printf: jest.fn()
        },
        createLogger: jest.fn().mockReturnValue(logger),
        transports: {
            Console: jest.fn()
        }
    })
);


describe("-- Logging Service --", () => {
    let loggerMock: winston.Logger;

    test('testing logger log function called...', () => {        
        const mockCreateLogger = jest.spyOn(winston, 'createLogger');
        const loggingService: LoggingService = LoggingService.Instance;
        loggerMock = mockCreateLogger.mock.instances[0];
        expect(loggingService).toBeInstanceOf(LoggingService)
        expect(loggingService).toBeDefined();
        expect(mockCreateLogger).toHaveBeenCalled()

        // spy on the winston.Logger instance within this test and check
        // that it is called - this is working from within the test method
        const logDebugMock = jest.spyOn(loggerMock, 'log');
        loggerMock.log('debug','test log debug');
        expect(logDebugMock).toHaveBeenCalled();

        // now try and invoke the logger instance indirectly through the service class
        // check that loggerMock is called a second time - this fails, only called once
        // from the preceding lines in this test
        loggingService.debug('debug message');
        expect(logDebugMock).toHaveBeenCalledTimes(2);
    });

   ...

LoggingService调试方法代码

public debug(message: string) {
        this.logger.log(
            {
                level: types.LogLevel.DEBUG,
                message: message,
                meta: {
                    context: this.contextName
                }
            }
        );
    }

更新:2019 年 3 月 9 日

重构我的 Nestjs LoggingService 以在构造函数中依赖注入 Winston 记录器实例以方便单元测试。这使我能够使用玩笑间谍软件在 Winston 记录器的 log 方法上并检查它是否已在服务实例中被调用:

// create winstonLoggerInstance here, e.g. in beforeEach()....
const winstonLoggerMock = jest.spyOn(winstonLoggerInstance, 'log');
serviceInstance.debug('debug sent from test');
expect(winstonLoggerMock).toHaveBeenCalled();

我已经测试了您的代码,似乎使用 jest.mock 存在多个问题。

为了正确模拟模块,您必须在导入它之前先模拟它。这是一种内部机制(jest 如何模拟模块),您必须遵循此规则。

const logger = {
  debug: jest.fn(),
  log: jest.fn()
};

// IMPORTANT First mock winston
jest.mock("winston", () => ({
  format: {
    colorize: jest.fn(),
    combine: jest.fn(),
    label: jest.fn(),
    timestamp: jest.fn(),
    printf: jest.fn()
  },
  createLogger: jest.fn().mockReturnValue(logger),
  transports: {
    Console: jest.fn()
  }
}));

// IMPORTANT import the mock after
import * as winston from "winston";
// IMPORTANT import your service (which imports winston as well)
import { LoggingService } from "../logger/logger.service";

正如您所看到的,您不能使用 Winston 实例作为模拟的返回值,但不用担心,也可以模拟该实例。 (您也可以在前面的代码示例中看到它)

const logger = {
  debug: jest.fn(),
  log: jest.fn()
};

最后,您不需要监视曾经模拟过的内容,因此只需直接询问模拟即可。

完整的代码在这里:

const logger = {
  debug: jest.fn(),
  log: jest.fn()
};

// trying to mock createLogger to return a specific logger instance
jest.mock("winston", () => ({
  format: {
    colorize: jest.fn(),
    combine: jest.fn(),
    label: jest.fn(),
    timestamp: jest.fn(),
    printf: jest.fn()
  },
  createLogger: jest.fn().mockReturnValue(logger),
  transports: {
    Console: jest.fn()
  }
}));

import * as winston from "winston";
import { LoggingService } from "./logger.service";

describe("-- Logging Service --", () => {
  let loggerMock: winston.Logger;

  test("testing logger log function called...", () => {
    const mockCreateLogger = jest.spyOn(winston, "createLogger");
    const loggingService: LoggingService = LoggingService.Instance;
    loggerMock = mockCreateLogger.mock.instances[0];
    expect(loggingService).toBeInstanceOf(LoggingService);
    expect(loggingService).toBeDefined();
    expect(mockCreateLogger).toHaveBeenCalled();

    // spy on the winston.Logger instance within this test and check
    // that it is called - this is working from within the test method
    logger.log("debug", "test log debug");
    expect(logger.log).toHaveBeenCalled();

    // now try and invoke the logger instance indirectly through the service class
    // check that loggerMock is called a second time - this fails, only called once
    // from the preceding lines in this test
    loggingService.debug("debug message");

    expect(logger.debug).toHaveBeenCalledTimes(1); // <- here
  });
});

我将最后的断言更改为一,因为我调用了log在测试中,并且debug在日志服务中。

这是我使用的记录器服务:

import * as winston from "winston";

export class LoggingService {
  logger: winston.Logger;

  static get Instance() {
    return new LoggingService();
  }

  constructor() {
    this.logger = winston.createLogger();
  }

  debug(message: string) {
    this.logger.debug(message);
  }
}

玩得开心!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Jest 模拟封装在服务类中的 Winston 记录器实例 的相关文章

随机推荐

  • 在 UNIX /usr/bin/script 中继承别名

    UNIX usr bin script 命令将创建 shell 会话的运行脚本 有关详细信息 请参阅 man script 然而 当inside一个脚本实例 它似乎忘记了父 shell 的环境变量 别名等 以下示例演示了如何在 script
  • 如何使用 groovy 创建和配置新的 Jenkins 作业?

    有很多 Groovy 脚本的示例 http scriptlerweb appspot com catalog list 但是我没有发现创造新就业机会的例子 网上有一个很好的例子来说明如何做到这一点吗 Create 来自 SCM 的管道脚本
  • 带有模拟的 Angular 2 TestBed

    我正在尝试测试使用其他服务的组件 我想通过为服务提供模拟来隔离组件 在 RC5 之前我可以简单地使用addproviders现在已弃用 并将在下一个 RC 中删除 相反 我必须使用TestBed 当我出于某种原因提供模拟角度时 请继续寻找模
  • 如何以编程方式测试路径/文件是否是快捷方式?

    我需要测试文件是否是快捷方式 我仍在尝试弄清楚如何设置内容 但我可能只有它的路径 我可能只有文件的实际内容 作为字节 或者我可能两者都有 一些复杂的情况包括它可能位于 zip 文件中 在这种情况下 路径将是内部路径 可以使用 SHELL32
  • 在 OS X 上设置区域设置会导致崩溃

    以下代码在 Linux 上运行良好 但在 OS X 10 7 上引发异常 include
  • 如何启动所有安卓厂商的电源管理器以启用后台和推送通知?

    由于制造商进行了自定义 Android 调整 某些 Android 设备在电源管理方面存在一些政治因素 从而破坏了推送通知等某些功能 华为 仅限 EMUI 5 0 Android 7 之前版本 前往 设置 gt 受保护的应用程序 检查您的应
  • bash/Makefile 中的双美元符号是什么意思?

    当在 Makefile 中插入 shell 脚本时 我们 使用双美元符号 来引用变量 为什么会这样 for number in 1 2 3 4 do echo number done As per gnu make 官方文档 配方中的变量和
  • 错误消息“错误:程序中存在杂散‘\302’”

    我在用着代码 块 on Ubuntu 10 10 特立独行的猫鼬 我已连接 Mac 键盘并将键盘设置设置为 瑞士德语 Mac 现在 每当我写一个等号 后跟一个空格 例如宽度 100 时 我都会收到错误消息 程序中出现杂散 302 我知道这个
  • MessageDlg 显示信息图标而不是确认

    在 Win 7 上 MessageDlg 显示信息图标而不是确认图标 问号 这是代码 MessageDlg Are you sure you want to delete this file CRLF FileName mtConfirma
  • 同时使用 JSR-303 和传统 Bean 验证?

    是否可以同时使用JSR 303 bean 验证 and 传统验证 该类型的单个验证器类 在 Spring 中 如果是这样 需要什么配置来设置它 我已经尝试过上面的说明参考 InitBinder protected void initBind
  • jsSHA、CryptoJS 和 OpenSSL 库给出不同的结果

    作为 JS 新手 我也在学习使用加密库 我不明白为什么使用相同的秘密对相同的消息进行签名 编码会产生不同的结果 我在用着jsSHA 1 3 1 在这里找到 and 这里描述了 CryptoJS 3 0 2尝试创建 Base64 sha 1
  • 如何检测 read.csv 的正确编码?

    我有这个文件 http b7hq6v alterupload com en 我想用 R 来读取它read csv 但我无法检测到正确的编码 好像是UTF 8的一种 我在 WindowsXP 计算机上使用 R 2 12 1 有帮助吗 首先基于
  • 全局变量 - 数据库连接?

    我试图连接到数据库 MySQLi 一次 但这样做时遇到问题 如何为整个脚本建立全局连接 有多个文件 index php classes config class php classes admin class php 等 我尝试过以下方法
  • 如何从 Android 应用程序发推文?

    我正在开发一个 Android 应用程序 并且希望具有在我的 Twitter 帐户 TWEET 上发送消息的功能 哪些 API 可用于此目的 代码示例将不胜感激 如果您只需要发送推文 这可以帮助您 String tweetUrl https
  • 任何 concurrent.futures 超时确实有效吗?

    尝试以便宜的方式编写基于进程的超时 同步 如下所示 from concurrent futures import ProcessPoolExecutor def call with timeout func args timeout 3 w
  • 如何使用 Google Drive API 一次删除多个文件

    我正在开发一个 python 脚本 它将文件上传到我的驱动器中的特定文件夹 我注意到 驱动器 api 为此提供了一个很好的实现 但我确实遇到了一个问题 如何一次删除多个文件 我尝试从驱动器中获取我想要的文件并组织它们的 ID 但没有运气 下
  • 当我有很多记录时,Postgres 函数返回一条记录?

    我有很多记录 我的简单查询返回 但当我使用函数时 它只给我第一条记录 首先我使用以下方法创建自己的数据类型 CREATE TYPE my type usr id integer name varchar 30 我的功能是 CREATE OR
  • PowerMockito:使用匹配器模拟静态方法时出现 InvalidUseOfMatchersException

    当我测试这个静态方法时 public class SomeClass public static long someMethod Map map String string Long l Log log with import org ap
  • 将文件保存到选定的目录中(javascript)

    你好 我的问题是关于javascript的 我想要1 要求用户选择一个目录2 然后将我的一堆文件写入其中 可能通过创建子目录 而不与用户交互 我怎样才能做到这一点 我可以吗 我是 javascript 新手 希望得到您的帮助 PS 我听说过
  • 如何使用 Jest 模拟封装在服务类中的 Winston 记录器实例

    我想嘲笑温斯顿记录器封装在使用 NestJS 创建的服务类中的实例 我在下面包含了我的代码 我无法从服务类中触发模拟的记录器实例 谁能解释我哪里出了问题 import as winston from winston import logge