如何在 ES6 中存根导出函数?

问题描述 投票:0回答:5

我有文件 foo.js:

export function bar (m) {
  console.log(m);
}

另一个使用 foo.js 的文件,cap.js:

import { bar } from 'foo';

export default m => {
  // Some logic that I need to test
  bar(m);
}

我有test.js:

import cap from 'cap'

describe('cap', () => {
  it('should bar', () => {
      cap('some');
  });
});

不知何故,我需要在测试中覆盖

bar(m)
的实现。有什么办法可以做到这一点吗?

附注我使用 babel、webpack 和 mocha。

javascript ecmascript-6
5个回答
100
投票

哎呀..我找到了解决方案,所以我使用

sinon
来存根并使用
import * as foo from 'foo'
来获取具有所有导出函数的对象,以便我可以存根它们。

import sinon from 'sinon';
import cap from 'cap';
import * as foo from 'foo';

sinon.stub(foo, 'bar', m => {
    console.log('confirm', m);
});

describe('cap', () => {
  it('should bar', () => {
    cap('some');
  });
});

10
投票

您只能从模块本身内部替换/重写/存根导出。 (这是解释

如果你像这样重写'foo.js':

var bar = function bar (m) {
  console.log(m);
};

export {bar}

export function stub($stub) {
  bar = $stub;
}

然后您可以在测试中覆盖它,如下所示:

import cap from 'cap'
import {stub} from 'foo'

describe('cap', () => {
  it('should bar', () => {
      stub(() => console.log('stubbed'));
      cap('some'); // will output 'stubbed' in the console instead of 'some'
  });
});

我创建了一个 Babel 插件,可以自动转换所有导出,以便可以对它们进行存根:https://github.com/asapach/babel-plugin-rewire-exports


3
投票

虽然 @Mike 解决方案可以在旧版本的 sinon 中工作,但自 sinon 3.0.0 以来它已被删除。

现在代替:

sinon.stub(obj, "meth", fn);

你应该做:

stub(obj, 'meth').callsFake(fn)

模拟 google oauth api 的示例:

import google from 'googleapis';

const oauth2Stub = sinon.stub(); 

sinon.stub(google, 'oauth2').callsFake(oauth2Stub);

oauth2Stub.withArgs('v2').returns({
    tokeninfo: (accessToken, params, callback) => {
        callback(null, { email: '[email protected]' }); // callback with expected result
    }
});

2
投票

您可以使用 babel-plugin-rewire (

npm install --save-dev babel-plugin-rewire
)

然后在

test.js
中使用导入模块上的
__Rewire__
函数来替换该模块中的函数:

// test.js
import sinon from 'sinon'

import cap from 'cap'

describe('cap', () => {
  it('should bar', () => {
    const barStub = sinon.stub().returns(42);
    cap.__Rewire__('bar', barStub); // <-- Magic happens here
    cap('some');
    expect(barStub.calledOnce).to.be.true;
  });
});

请务必将

rewire
添加到
.babelrc
中的 babel 插件中:

// .babelrc
{
  "presets": [
    "es2015"
  ],
  "plugins": [],
  "env": {
    "test": {
      "plugins": [
        "rewire"
      ]
    }
  }
}

最后,正如您所看到的,

babel-plugin-rewire
插件仅在测试环境中启用,因此您应该将
BABEL_ENV
环境变量设置为
test
(您可能已经这样做了)来调用测试运行程序:

env BABEL_ENV=test mocha --compilers js:babel-core/register test-example.js

注意:我无法让

babel-plugin-rewire-exports
去工作。


0
投票

这对我来说也绝对是一个陷阱......

我创建了一个小实用程序来解决 sinon 的这个限制。 (也可在 js 中使用)。

// mockable.ts 👇

import sinon from 'sinon'

export function mockable<T extends unknown[], Ret>(fn: (...fnArgs: T) => Ret) {
  let mock: sinon.SinonStub<T, Ret> | undefined
  const wrapper = (...args: T) => {
    if (mock) return mock(...args)
    return fn(...args)
  }
  const restore = () => {
    mock = undefined
  }
  wrapper.mock = (customMock?: sinon.SinonStub<T, Ret>) => {
    mock = customMock || sinon.stub()
    return Object.assign(mock, { restore })
  }
  wrapper.restore = restore
  return wrapper
}

如果将上面的代码片段粘贴到您的项目中,您可以像这样使用它

foo.js

import { mockable } from './mockable'

// we now need to wrap the function we wish to mock
export const foo = mockable((x) => {
   console.log(x)
})

main.js

import { foo } from './foo'

export const main = () => {
  foo('asdf') // use as normal
}

测试.js

import { foo } from './foo'
import { main } from './main'

// mock the function - optionally pass in your own mock
const mock = foo.mock()

// test the function
main()
console.assert(mock.calledOnceWith('asdf'), 'not called')

// restore the function
stub.restore()

这种方法的好处是您不必记住始终以某种方式导入函数。

import { foo } from './foo'
import * as foo from './foo'
一样有效。自动导入可能只适用于您的 IDE。

© www.soinside.com 2019 - 2024. All rights reserved.