【问题标题】:global Jest SpyOn function doesen't call the original function全局 Jest SpyOn 函数不调用原始函数
【发布时间】:2021-11-03 18:23:55
【问题描述】:

希望有人能帮助我理解js原型和jest.spOn()的交互性。

我有一个小例子: 文件TestObj.ts中的示例类:

export default class TestObj {
  foo() {
    // Do Something e.g.
    console.log("Hello World!");
  }
}

以下示例测试用例成功,但从未执行 console.log

import TestObj from './TestObj';

const spyObj = jest.spyOn(TestObj.prototype, 'foo');
test('debug test', () => {
  const obj = new TestObj();
  obj.foo();
  expect(spyObj).toHaveBeenCalled();
});

如果我将示例测试用例更改为以下内容,则测试成功并按预期调用 console.log 语句。

import TestObj from './TestObj';

test('debug test', () => {
  const spyObj = jest.spyOn(TestObj.prototype, 'foo');
  const obj = new TestObj();
  obj.foo();
  expect(spyObj).toHaveBeenCalled();
});

知道为什么使用全局 spyOn 变量的版本不能按预期工作吗?


编辑:

它似乎与原型无关。 没有任何类的函数也存在同样的问题, 将 First Code sn-p (TestObj.ts) 编辑为:

export foo() {
  // Do Something e.g.
  console.log("Hello World!");
};

对于更新的第二个截图,我们收到了同样的问题。 (测试成功,但始终无法到达控制台日志。)

import * as testlib from './TestObj';

const spyObj = jest.spyOn(testlib, 'foo');
test('debug test', () => {
  testlib.foo();
  expect(spyObj).toHaveBeenCalled();
});

但是,如果我们将第二个 sn-p 更新为以下内容,则测试成功并执行控制台日志:

import * as testlib from './TestObj';

const spyObj: jest.SpyInstance;

beforeEach(() => {
  spyObj = jest.spyOn(testlib, 'foo');
});
test('debug test', () => {
  testlib.foo();
  expect(spyObj).toHaveBeenCalled();
});

但是我仍然不知道为什么会发现这个问题。

【问题讨论】:

    标签: javascript typescript testing jestjs


    【解决方案1】:

    谁遇到过这篇文章,

    问题说明

    我做了很多研究(try&error、mdn、jest manpage 和很多媒体文章),我想我找到了奇怪行为的原因。要理解这个问题,了解以下几点很重要:

    • 1:JS原型是全局变量,对应类型的每个对象都依赖。
    • 2:Jest 不会在每次测试后重置全局变量,之前或在任何测试中对全局变量所做的更改将保留在整个测试套件(文件)中。
    • 3:jest spy on 函数实际上是指定函数的模型,其实现调用了它自己的函数。例如。: jest.SpyOn(TestObj.prototype, 'foo'); 实际上实现为:TestObj.prototype.foo = new jest.fn().mockImplementation(()=>{original_TestObj.prototype.foo()}); 这意味着监视类原型的函数实际上是在更改全局变量。
    • 4:根据您的 jest 配置,可以在每次测试之前将模型函数重置为默认值。但要注意spyOn 的默认函数似乎与jest.fn() 相同,它本身是一个空实现,这意味着模型仍然可以调用但没有代码,尤其是没有执行原始实现。李>

    解决方案

    • 如果您希望测试用例彼此独立,请避免更改全局变量。
    • 避免监视原型,在测试用例中,如果您仅在单个测试中需要监视,请尝试监视本地对象,例如:
    test('should foo', () => {
      const testObj = new TestObj();
      const spyOnFn = jest.spyOn(testObj, 'foo');
    
      // Do anything 
    
      expect(spyOnFn).to//Have been anything
    });
    
    • 如果需要相同功能的spyOn 实现 在多个测试中尝试为测试创建一个全局变量 但是使用 jest 的每个功能之前设置间谍。这 功能在所有模拟重置后执行(如果启用)。 例如:
    let spyOnFunction1: jest.SpyInstance;
    
    beforeEach(()=> {
      spyOnFunction1 = jest.spyOn(TestObj.prototype, 'foo');
    });
    

    【讨论】:

    • 我不明白它与第二个与原型无关的示例有何关系。你能详细说明为什么spyObj = jest.spyOn(testlib, 'foo'); 必须在beforeEach 里面吗?
    • 与全局变量有关,与原型无关。是否存在全局变量并不重要,它是模块对象的一部分,还是类实例(对象)的一部分并与原型相关。 beforeEach 块实际上并不是真正的解决方案,而是某种可行的技巧。您仍然会覆盖全局变量,但您会在每个测试用例之前使用新实例执行此操作,以便它可以工作。
    猜你喜欢
    • 2017-11-29
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-02
    相关资源
    最近更新 更多