使用Sinon 对单个导出函数进行存根

Vot*_*ike 2 javascript testing sinon

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

Bri*_*ams 11

这个语法:

import something from 'myModule';
Run Code Online (Sandbox Code Playgroud)

...是绑定somethingdefault' 'myModule.

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

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

// ...

const stub = sinon.stub(myModule, 'default');
Run Code Online (Sandbox Code Playgroud)

...但这仅适用于'myModule'ES6 模块。

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

module.exports = debounce;
Run Code Online (Sandbox Code Playgroud)

...这意味着模块导出去抖功能。

这意味着为了存根,'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 })
Run Code Online (Sandbox Code Playgroud)

...或者如果您正在使用,Jest您可以使用类似的东西jest.mock

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

细节

仅当模块是 ES6 模块时,对模块的导出进行default存根处理才有效,这是因为编译期间发生的情况。

ES6 语法被编译为 ES6 之前的 JavaScript。例如,Babel 将其转变为:

import something from 'myModule';
Run Code Online (Sandbox Code Playgroud)

...到这个:

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
}
Run Code Online (Sandbox Code Playgroud)

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

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