【问题标题】:Unable to stub a function using Sinon无法使用 Sinon 存根函数
【发布时间】:2017-09-22 09:49:05
【问题描述】:

我有一个 Redux 操作,它本身会调度另外两个操作。每个动作都是从导入的函数中检索的。一个来自本地模块,另一个来自外部库。

import { functionA } from './moduleA';
import { functionB } from 'libraryB';

export function myAction() {
  return (dispatch) => {
    dispatch(functionA());
    ...
    dispatch(functionB());
  }
}

在我的测试中,我使用sinon 沙箱来存根函数,但只有两个测试通过。我希望所有 3 个都通过。

import * as A from './moduleA';
import * as B from 'libraryB';

describe(__filename, async () => {
  it('Calls 2 other actions', () => {
    sandbox = sinon.sandbox.create();

    const dispatch = sandbox.stub();

    sandbox.stub(A, 'functionA');
    sandbox.stub(B, 'functionB');

    await myAction()(dispatch);

    // passes
    expect(dispatch.callCount).to.equal(2);

    //passes
    expect(A.functionA).to.have.been.called();

    // fails
    expect(B.functionB).to.have.been.called();     
  });
});

最后一个期望失败并出现错误:

TypeError: [Function: functionB] 不是间谍或对间谍的调用!

当我将函数输出到控制台时,我得到了这个,这似乎与 Babel 导入导出导出的方式有关 (ES6 re-exported values are wrapped into Getter)。这些功能可以实时工作,而不是在测试中。

{ functionA: [Function: functionA] }
{ functionB: [Getter] }

我尝试过使用stub.get(getterFn),但这只会给我一个错误:

TypeError:无法重新定义属性:fetchTopicAnnotations

【问题讨论】:

    标签: javascript redux sinon


    【解决方案1】:

    您是否尝试过命名存根?你的代码读起来有点奇怪。您在测试中的任何时候都没有提到您的存根。

    import * as A from './moduleA';
    import * as B from 'libraryB';
    
    describe(__filename, async () => {
      it('Calls 2 other actions', () => {
        sandbox = sinon.sandbox.create();
    
        const dispatch = sandbox.stub();
    
        const functionAStub = sandbox.stub(A, 'functionA');
        const functionBStub = sandbox.stub(B, 'functionB');
    
        await myAction()(dispatch);
    
        // passes
        expect(dispatch.callCount).to.equal(2);
    
        //passes
        expect(functionAStub.called).toBe(true);
    
        // fails
        expect(functionBStub.called).toBe(true);     
      });
    });
    

    【讨论】:

    • 感谢您的回答,但命名存根没有任何作用
    【解决方案2】:

    很难说 100%,但似乎模块正在导入,因此在您的测试存根之前直接引用了该函数。 Sinon 将有效地替换导出对象上的函数,但其​​他模块将首先导入并获取对实际函数的引用,替换它不会导致重新加载。

    您可以对其进行试验并通过将模块更改为以下方式来证明它:

    import * as A from './moduleA';
    import * as B from 'libraryB';
    
    export function myAction() {
      return (dispatch) => {
        dispatch(A.functionA());
        ...
        dispatch(B.functionB());
      }
    }
    

    这实际上将在调用时查找对 functionA/functionB 的引用,这将允许 sining 用存根替换它们。

    如果您觉得这很恶心并想保留原始表单,那么我认为您需要使用名为 proxyquire 的第二个库,它可以有效地让您存根正在导入的整个模块。

    您的测试函数最终看起来更像这样:

    import * as proxyquire from 'proxyquire'
    // import * as A from './moduleA'
    // import * as B from 'libraryB'
    
    describe(__filename, async () => {
      const A = {}
      const B = {}
      const functionAStub = sandbox.stub(A, 'functionA')
      const functionBStub = sandbox.stub(B, 'functionB')
      const { myAction } = proxyquire('./myAction', {
        './moduleA': A,
        'libraryB': B
      })
      ...
    })
    

    被测模块在你调用 proxyquire 之前不会被导入,当它需要模块时,它将使用你的存根模块而不是真正的模块。

    然后,您可以按预期从测试中引用这些存根。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-20
      • 1970-01-01
      • 1970-01-01
      • 2017-06-02
      • 2020-03-29
      • 1970-01-01
      • 1970-01-01
      • 2019-02-21
      相关资源
      最近更新 更多