简体   繁体   English

用 Jest 模拟返回函数的 node_modules?

[英]Mocking node_modules which return a function with Jest?

I am writing a typeScript program which hits an external API.我正在编写一个打到外部 API 的 typeScript 程序。 In the process of writing tests for this program, I have been unable to correctly mock-out the dependency on the external API in a way that allows me to inspect the values passed to the API itself.在为这个程序编写测试的过程中,我一直无法正确模拟对外部 API 的依赖,以允许我检查传递给 API 本身的值。

A simplified version of my code that hits the API is as follows:命中 API 的我的代码的简化版本如下:

const api = require("api-name")();

export class DataManager {
  setup_api = async () => {
    const email = "email@website.ext";
    const password = "password";
    try {
      return api.login(email, password);
    } catch (err) {
      throw new Error("Failure to log in: " + err);
    }
  };

My test logic is as follows:我的测试逻辑如下:

jest.mock("api-name", () => () => {
  return {
    login: jest.fn().mockImplementation(() => {
      return "200 - OK. Log in successful.";
    }),
  };
});

import { DataManager } from "../../core/dataManager";
const api = require("api-name")();

describe("DataManager.setup_api", () => {
  it("should login to API with correct parameters", async () => {
    //Arrange
    let manager: DataManager = new DataManager();

    //Act
    const result = await manager.setup_api();

    //Assert
    expect(result).toEqual("200 - OK. Log in successful.");
    expect(api.login).toHaveBeenCalledTimes(1);
  });
});

What I find perplexing is that the test assertion which fails is only expect(api.login).toHaveBeenCalledTimes(1) .我发现令人困惑的是,失败的测试断言只是expect(api.login).toHaveBeenCalledTimes(1) Which means the API is being mocked, but I don't have access to the original mock.这意味着 API 正在被模拟,但我无权访问原始模拟。 I think this is because the opening line of my test logic is replacing login with a NEW jest.fn() when called.我认为这是因为我的测试逻辑的开头行在调用时用 NEW jest.fn()替换了login Whether or not that's true, I don't know how to prevent it or to get access to the mock function-which I want to do because I am more concerned with the function being called with the correct values than it returning something specific.无论这是否属实,我不知道如何阻止它或访问模拟函数——我想这样做是因为我更关心使用正确值调用的函数,而不是它返回特定的东西。

I think my difficulty in mocking this library has to do with the way it's imported: const api = require("api-name")();我认为我嘲笑这个库的困难与它的导入方式有关: const api = require("api-name")(); where I have to include an opening and closing parenthesis after the require statement.我必须在 require 语句之后包含一个左括号和右括号。 But I don't entirely know what that means, or what the implications of it are re:testing.但我不完全知道这意味着什么,或者它的含义是什么:重新测试。

I came across an answer in this issue thread for ts-jest.我在这个问题线程中找到了 ts-jest 的答案。 Apparently, ts-jest does NOT "hoist" variables which follow the naming pattern mock* , as regular jest does.显然, ts-jest 不会像常规 jest 那样“提升”遵循命名模式mock*变量。 As a result, when you try to instantiate a named mock variable before using the factory parameter for jest.mock() , you get an error that you cannot access the mock variable before initialization.因此,当您在使用jest.mock()factory参数之前尝试实例化一个命名的模拟变量时,您会收到一个错误,提示您在初始化之前无法访问模拟变量。

Per the previously mentioned thread, the jest.doMock() method works in the same way as jest.mock() , save for the fact that it is not "hoisted" to the top of the file.根据前面提到的线程, jest.doMock()方法的工作方式与jest.mock()相同,除了它没有“提升”到文件顶部的事实。 Thus, you can create variables prior to mocking out the library.因此,您可以在模拟库之前创建变量。

Thus, a working solution is as follows:因此,一个有效的解决方案如下:

const mockLogin = jest.fn().mockImplementation(() => {
  return "Mock Login Method Called";
});

jest.doMock("api-name", () => () => {
  return {
    login: mockLogin,
  };
});

import { DataManager } from "../../core/dataManager";

describe("DataManager.setup_api", () => {
  it("should login to API with correct parameters", async () => {
    //Arrange
    let manager: DataManager = new DataManager();

    //Act
    const result = await manager.setup_api();

    //Assert
    expect(result).toEqual("Mock Login Method Called");
    expect(mockLogin).toHaveBeenCalledWith("email@website.ext", "password");
  });
});

Again, this is really only relevant when using ts-jest , as using babel to transform your jest typescript tests WILL support the correct hoisting behavior.同样,这仅在使用ts-jest时才相关,因为使用babel转换您的 jest 打字稿测试将支持正确的提升行为。 This is subject to change in the future, with updates to ts-jest , but the jest.doMock() workaround seems good enough for the time being.这在未来可能会发生变化,更新ts-jest ,但jest.doMock()解决方法jest.doMock()似乎已经足够了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM