【问题标题】:Stubbing single exported function with Sinon使用 Sinon 对单个导出函数进行存根
【发布时间】:2019-10-28 21:51:55
【问题描述】:

我刚刚将我的 lodash 导入从 import _ from 'lodash'; 更改为 import debounce from 'lodash/debounce';
在我的测试中,我曾经拥有sandbox.stub(_, 'debounce').returnsArg(0);,但现在我不知道要改成什么。显然sandbox.stub(debounce).returnsArg(0); 不起作用。当一个模块只导出一个函数时,不知道该怎么做。

【问题讨论】:

标签: javascript testing sinon


【解决方案1】:

这个语法:

import something from 'myModule';

...是将something 绑定到'myModule' 的default 导出的ES6 语法。

如果模块是 ES6 模块,那么您可以像这样对模块的 default 导出存根:

import * as myModule from 'myModule';
const sinon = require('sinon');

// ...

const stub = sinon.stub(myModule, 'default');

...但这仅在 'myModule' 是 ES6 模块时才有效。

在这种情况下,'lodash/debounce' 不是 ES6 模块,它是预编译的。最后一行是这样的:

module.exports = debounce;

...表示模块导出去抖动功能。

这意味着为了存根'lodash/debounce',您必须模拟整个模块。

Sinon 不提供模块级别的模拟,因此您需要使用类似proxyquire:

const proxyquire = require('proxyquire');
const sinon = require('sinon');

const debounceStub = sinon.stub().returnsArg(0);
const code = proxyquire('[path to your code]', { 'lodash/debounce': debounceStub })

...或者如果你使用Jest,你可以使用jest.mock

jest.mock('lodash/debounce', () =>
  jest.fn((func, ms) => func)  // <= mock debounce to simply return the function
);

详情

仅当模块是 ES6 模块时才对模块的 default 导出进行存根处理的原因是编译期间发生的事情。

ES6 语法被编译成 pre-ES6 JavaScript。例如,Babel 会变成这样:

import something from 'myModule';

...进入这个:

var _myModule = _interopRequireDefault(require("myModule"));

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ?
    obj :  // <= return the result of require("myModule") if it is an ES6 module...
    { default: obj };  // <= otherwise set it to the default property of a wrapper object
}

...所以如果'myModule' 是一个 ES6 模块,它会直接返回...但如果不是,则互操作会返回一个包装对象。

由于每个import 获得不同的包装对象,因此更改一个default 属性不会影响任何其他default 属性。

【讨论】:

  • 太棒了。我认为这需要一个新的库或其他东西。
【解决方案2】:

您可以为自己创建一个包装文件,该文件最终将为您导出相同的 lodash/debounce 实例,但这次您可以存根,例如:

myutils/lodash/debounce.js

import lodashDebounce from 'lodash/debounce';

const exports = {
    debounce: lodashDebounce,
};

export const debounce = () => exports.debounce();

export default exports;

现在,在您的实际代码中,不是从原始位置导入 debounce,而是从这个包装文件中导入:

之前:

import debounce from 'lodash/debounce' // this is how we usually do

之后:

import { debounce } from 'myutils/lodash/debounce' // if we want to stub it


// all other code-lines remain the same
const method = () => {
    debounce(callback, 150));
    ...
}

现在在做 test.js 时:

import lodashWrapped from 'myutils/lodash/debounce';

sinon.stub(lodashWrapped , 'debounce').callsFake((callbackFn) => {
  // this is stubbed now
});

// go on, make your tests now 

【讨论】:

    猜你喜欢
    • 2017-06-02
    • 1970-01-01
    • 2020-07-07
    • 2017-06-05
    • 1970-01-01
    • 1970-01-01
    • 2021-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多