【问题标题】:jest.spyOn function which will be called after returning the result返回结果后调用的 jest.spyOn 函数
【发布时间】:2019-02-21 20:24:55
【问题描述】:

考虑我们有一些方法的类。我们想监视方法 B 是否被调用,而一旦执行方法 A 将返回结果给调用者,方法 B 将异步执行。该类如下所示。

class Parent {
    constructor() {
        this.conf = [{ id:'1', func: Parent.methodA.bind(Parent)}, { id:'2', func: Parent.methodB.bind(Parent)}]
    }
    static methodA() {
        console.log('methodA is called from methodC');
        return 'methodA';
    }
    static methodB() {
        console.log('methodB is called from methodC');
        return 'methodB';
    }
    methodC(conf) {
        conf.forEach((eachConf) => {
            if(eachConf.id === '1') {
                setImmediate(() => {
                    // Just to call method with conf = [{ id:'2', func: this.methodB.bind(this)}]
                    this.methodC(conf.slice(1));
                })
                return eachConf.func();
            }
            return eachConf.func();
        });
    }
}
module.exports = Parent;

在 jest 测试文件中,调用 methodC 并希望确保 methodA 具有返回值并希望确保 methodB 也被调用。 testSpy.js const Parent = require('./parent');

it('check methodA and methodB called', async () => {
    const methodA = jest.spyOn(Parent, 'methodA');
    const methodB = jest.spyOn(Parent, 'methodB');
    const instance = new Parent();
    instance.methodC(instance.conf);
    expect(methodA).toHaveBeenCalled();
    //How to spyOn methodB.
    //expect(methodB).toHaveBeenCalled();
});

返回结果后可以运行多个方法。想要确保所有的都被执行,最好也能得到每一步的结果。

【问题讨论】:

    标签: jestjs


    【解决方案1】:

    .bind creates a new function 所以func 设置为this.methodA.bind(this) 创建的新函数。

    jest.spyOn(Parent, 'methodA'); 运行时,它会将methodA 替换为间谍,但这对func 没有任何影响。

    func 被调用时,新函数被调用,methodA 上的间谍永远不会被调用。


    如果您想使用spy 来验证是否调用了methodA,则必须确保调用它。

    在这种情况下,您可以在箭头函数中调用this.methodA,而不是使用bind(创建一个新函数)将methodA绑定到this(因为箭头函数capture the this of the enclosing lexical scope...in这种情况下,来自构造函数的this):

    constructor() {
      this.conf = [
        {
          id: '1',
          func: (...args) => this.methodA(...args)
        },
        {
          id: '2',
          func: (...args) => this.methodB(...args)
        }
      ];
    }
    

    然后当func 被调用时,它会调用methodA,而methodA 将调用spy 并且测试将通过。


    更新

    这是您更新后的代码示例的工作测试:

    it('check methodA and methodB called', async () => {
      jest.useFakeTimers();
      const methodA = jest.spyOn(Parent, 'methodA');
      const methodB = jest.spyOn(Parent, 'methodB');
      const instance = new Parent();
      instance.methodC(instance.conf);
      jest.runAllTimers();
      expect(methodA).toHaveBeenCalledTimes(1);  // SUCCESS
      expect(methodB).toHaveBeenCalledTimes(2);  // SUCCESS
    });
    

    jest.useFakeTimers 用可以记住它们被调用的模拟替换像 setImmediate 这样的计时器函数。然后jest.runAllTimers 运行所有使用计时器函数安排的回调。

    methodA 在第一次调用 methodC 时被调用。

    methodB 在第一次调用 methodC 时被调用,并且在由于 setImmediate 回调而再次调用 methodC 时也被调用。

    【讨论】:

    • 好的,谢谢。窥探我以不同的方式定义的功能。但我的问题是我们如何监视一些在我返回后异步运行的函数。注意:要异步运行函数,我在这里使用 setImmediate。
    • @jerry 请用新代码更新您的问题,我将用如何测试异步调用更新我的答案。 (请注意,您问题中的代码实际上从未调用过methodB
    • @brian-lives-outdoors.更新了问题
    • @jerry 您是否要测试 methodB 是否被调用? ...因为您的代码没有调用methodBthis.conf 中的 methodB 条目具有 id2 和您的 if 语句仅针对 1id 运行,因此只有 methodA 会被调用。
    • 是的,现在更新了。因为它是示例代码,所以我错过了
    【解决方案2】:

    当您使用setImmediate 时,您需要使用jest.useFakeTimers()jest.runAllTimers() 来立即调用您放入setImmediate 的每个回调。

    it('check methodA and methodB called', async () => {
        jest.useFakeTimers()
        const methodA = jest.spyOn(Parent, 'methodA');
        const methodB = jest.spyOn(Parent, 'methodB');
        Parent.methodC(Parent.conf);
        jest.runAllTimers()
        expect(methodA).toHaveBeenCalled();
        //How to spyOn methodB.
        //expect(methodB).toHaveBeenCalled();
    });
    
    

    【讨论】:

    • 即使使用 jestUseFakeTimers() 和 jest.runAllTimers(),也无法监视返回后执行的所有函数。如果我在 return 语句之后运行了多个函数,它会监视第一个函数。不适合所有人。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-05
    • 2017-03-18
    • 1970-01-01
    • 2022-12-22
    • 1970-01-01
    • 1970-01-01
    • 2017-04-30
    相关资源
    最近更新 更多