模拟会返回带有Jest函数的node_modules吗?

时间:2020-10-28 16:01:15

标签: javascript typescript unit-testing jestjs

我正在编写一个使用外部API的typeScript程序。在为该程序编写测试的过程中,我无法正确地模拟出对外部API的依赖性,从而无法检查传递给API本身的值。

命中该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);
    }
  };

我的测试逻辑如下:

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);
  });
});

我感到困惑的是,失败的测试断言只是expect(api.login).toHaveBeenCalledTimes(1)。这意味着该API已被模拟,但我无权访问原始模拟。我认为这是因为测试逻辑的开头是在调用时用新的login来代替jest.fn()。不管这是真的,我不知道如何防止它或无法访问模拟函数,这是我想做的,因为我更关心的是用正​​确的值调用该函数,而不是返回特定的值。 / p>

我认为模拟该库的困难与它的导入方式有关:const api = require("api-name")();,在require语句之后,必须在其中加上一个左括号和右括号。但是我并不完全知道这意味着什么,或者它意味着重新测试。

1 个答案:

答案 0 :(得分:0)

我在this issue thread中碰到了ts-jest的答案。显然,ts-jest不会像常规的玩笑一样“提升”遵循命名模式mock*的变量。结果,当您尝试为factory使用jest.mock()参数之前实例化命名的模拟变量时,会收到一个错误,指出您无法在初始化之前访问该模拟变量。

对于每个前面提到的线程,jest.doMock()方法的工作方式与jest.mock()相同,不同之处在于它没有“提升”到文件的顶部。因此,您可以在模拟库之前创建变量。

因此,一种可行的解决方案如下:

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");
  });
});

同样,这仅在使用ts-jest时才有意义,因为使用babel转换笑话打字稿测试将支持正确的举升行为。将来,随着ts-jest的更新,这种情况可能会有所变化,但是jest.doMock()的解决方法暂时看来还不错。