Javascript 在函数中模拟函数并获得调用计数

Javascript 在函数中模拟函数并获得调用计数,javascript,unit-testing,mocking,jestjs,Javascript,Unit Testing,Mocking,Jestjs,在script.js中考虑此模块: const randomizeRange = (min, max) => { return Math.floor(Math.random() * (max - min) + min); }; const randomArr = (n, min, max) => { const arr = []; for (let i = 0; i < n; i++) { arr.push(randomizeRange(min, max)

script.js
中考虑此模块:

const randomizeRange = (min, max) => {
  return Math.floor(Math.random() * (max - min) + min);
};

const randomArr = (n, min, max) => {
  const arr = [];
  for (let i = 0; i < n; i++) {
    arr.push(randomizeRange(min, max));
  }
  return arr;
};


module.exports = { randomArr, randomizeRange };

我很感激你试图理解这个问题,所以我会尽我所能回答你。我通常建议您不要模拟函数,除非您正在对服务器或数据库或其他对象进行某种复杂的后端调用。您可以使用真实代码(而不是模拟代码)进行更真实的测试,这将带来无穷的好处。这将是我给你今后的建议

但是为了这个问题的目的-这里是发生了什么

您没有看到模拟以正确的方式工作的原因是,您实际上创建了两个独立的模拟:

  • 您已经为
    randomizarr
    创建了一个模拟
  • 您已经为
    randomizerage
    创建了另一个单独的模拟
当您在测试中调用
randomArrMock(100,20,1000)
时,这将调用您指定的
randomizerar
的单个mock,但该mock绝对没有
randomizerage
的其他mock的概念。所以它从不在幕后调用。因此,这就是为什么您在末尾看到调用量为
0

这里只需稍微更改一下设置,指定调用了哪些mock,以及何时调用,以便看到这两者按照您的预期协同工作

实际上,我在这里选择的是而不是来模拟你的
随机化方法。该方法调用了
Math.floor
,在本例中,这将更容易“监视”(这是对我们将要使用的方法的提示)。该
Math.floor
方法在每次调用
randomizerage
时都会被调用一次,因此它将是一个理想的选择。它也应该被调用
100次
,与您的方法相同

我们之所以选择
spyOn
方法,而不是像您已经做过的那样使用实际的模拟,是因为我们希望保持原始行为,因此在不覆盖预期行为的情况下监视方法被调用的次数是理想的

因此,请将现有的模拟对象保留为
randomizarr
,如下所示:

const randomArrMock = jest.fn(randomArr);
现在为
Math.floor

const mathFloorSpy = jest.spyOn(Math.prototype, `floor`);
此“间谍”将有效地监视此方法,而不会覆盖其行为。如果愿意,您可以覆盖该行为,但我们希望在计算调用次数的同时保持该行为的完整性

现在,当您运行测试套件时:

test("tests for randomArr", () => {
  expect(randomArrMock(100, 20, 1000)).toHaveLength(100);
  expect(randomArrMock.mock.calls.length).toBe(1)
  expect(mathFloorSpy.mock.calls.length).toBe(100)
});
现在这将作为
Math传递。每次调用
randomizerage
方法时,都会调用floor

最后,当您的测试完成时,不要忘记使用以下方法恢复原始的
Math.floor
功能:

mathFloorSpy.mockRestore();

快乐测试

我很感激你试图理解这个问题,所以我会尽我所能回答你。我通常建议您不要模拟函数,除非您正在对服务器或数据库或其他对象进行某种复杂的后端调用。您可以使用真实代码(而不是模拟代码)进行更真实的测试,这将带来无穷的好处。这将是我给你今后的建议

但是为了这个问题的目的-这里是发生了什么

您没有看到模拟以正确的方式工作的原因是,您实际上创建了两个独立的模拟:

  • 您已经为
    randomizarr
    创建了一个模拟
  • 您已经为
    randomizerage
    创建了另一个单独的模拟
当您在测试中调用
randomArrMock(100,20,1000)
时,这将调用您指定的
randomizerar
的单个mock,但该mock绝对没有
randomizerage
的其他mock的概念。所以它从不在幕后调用。因此,这就是为什么您在末尾看到调用量为
0

这里只需稍微更改一下设置,指定调用了哪些mock,以及何时调用,以便看到这两者按照您的预期协同工作

实际上,我在这里选择的是而不是来模拟你的
随机化方法。该方法调用了
Math.floor
,在本例中,这将更容易“监视”(这是对我们将要使用的方法的提示)。该
Math.floor
方法在每次调用
randomizerage
时都会被调用一次,因此它将是一个理想的选择。它也应该被调用
100次
,与您的方法相同

我们之所以选择
spyOn
方法,而不是像您已经做过的那样使用实际的模拟,是因为我们希望保持原始行为,因此在不覆盖预期行为的情况下监视方法被调用的次数是理想的

因此,请将现有的模拟对象保留为
randomizarr
,如下所示:

const randomArrMock = jest.fn(randomArr);
现在为
Math.floor

const mathFloorSpy = jest.spyOn(Math.prototype, `floor`);
此“间谍”将有效地监视此方法,而不会覆盖其行为。如果愿意,您可以覆盖该行为,但我们希望在计算调用次数的同时保持该行为的完整性

现在,当您运行测试套件时:

test("tests for randomArr", () => {
  expect(randomArrMock(100, 20, 1000)).toHaveLength(100);
  expect(randomArrMock.mock.calls.length).toBe(1)
  expect(mathFloorSpy.mock.calls.length).toBe(100)
});
现在这将作为
Math传递。每次调用
randomizerage
方法时,都会调用floor

最后,当您的测试完成时,不要忘记使用以下方法恢复原始的
Math.floor
功能:

mathFloorSpy.mockRestore();

快乐测试

这似乎是一个常见的问题,我能找到的最“正式”的解决方案是在Jest GitHub问题页面上的

因为您使用的是
require
而不是