如何使用 Jest 模拟 ES6 模块导入?

新手上路,请多包涵

我想测试我的一个 ES6 模块是否以特定方式调用另一个 ES6 模块。有了 茉莉花,这非常容易——

申请代码:

// myModule.js
import dependency from './dependency';

export default (x) => {
  dependency.doSomething(x * 2);
}

和测试代码:

//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    spyOn(dependency, 'doSomething');

    myModule(2);

    expect(dependency.doSomething).toHaveBeenCalledWith(4);
  });
});

Jest 的等价物是什么?我觉得这是一件很简单的事情,但我一直在努力解决这个问题。

我最接近的方法是将 import 替换为 require ,并将它们移动到测试/函数中。这两件事都不是我想做的。

// myModule.js
export default (x) => {
  const dependency = require('./dependency'); // Yuck
  dependency.doSomething(x * 2);
}

//myModule-test.js
describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    jest.mock('../dependency');

    myModule(2);

    const dependency = require('../dependency'); // Also yuck
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

对于奖励积分,当 dependency.js 中的函数是默认导出时,我很乐意使整个工作正常进行。但是,我知道监视默认导出在 Jasmine 中不起作用(或者至少我永远无法让它起作用),所以我也不希望它在 Jest 中是可能的。

原文由 Cam Jackson 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

快进到 2020 年,我发现这篇博文是解决方案: Jest mock default and named export

仅使用 ES6 模块语法:

 // esModule.js
 export default 'defaultExport';
 export const namedExport = () => {};

 // esModule.test.js
 jest.mock('./esModule', () => ({
 __esModule: true, // this property makes it work
 default: 'mockedDefaultExport',
 namedExport: jest.fn(),
 }));

 import defaultExport, { namedExport } from './esModule';
 defaultExport; // 'mockedDefaultExport'
 namedExport; // mock function

您还需要知道的一件事(我花了一段时间才弄清楚)是您不能在测试中调用 jest.mock() ;您必须在模块的顶层调用它。但是,如果您想为不同的测试设置不同的模拟,您可以在单个测试中调用 mockImplementation()。

原文由 Andy 发布,翻译遵循 CC BY-SA 4.0 许可协议

编辑:几年过去了,这不再是正确的方法(而且可能从来都不是,我的错)。

改变导入的模块是令人讨厌的,并且可能导致副作用,例如根据执行顺序通过或失败的测试。

出于历史目的,我将以原始形式保留此答案,但您应该真正使用 jest.spyOnjest.mock 。有关详细信息,请参阅本页上的笑话文档或其他答案。

原回答如下:


我已经能够通过使用涉及 import * 的 hack 来解决这个问题。它甚至适用于命名导出和默认导出!

对于命名导出:

 // dependency.js
export const doSomething = (y) => console.log(y)

 // myModule.js
import { doSomething } from './dependency';

export default (x) => {
  doSomething(x * 2);
}

 // myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.doSomething = jest.fn(); // Mutate the named export

    myModule(2);

    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

或者对于默认导出:

 // dependency.js
export default (y) => console.log(y)

 // myModule.js
import dependency from './dependency'; // Note lack of curlies

export default (x) => {
  dependency(x * 2);
}

 // myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.default = jest.fn(); // Mutate the default export

    myModule(2);

    expect(dependency.default).toBeCalledWith(4); // Assert against the default
  });
});

原文由 Cam Jackson 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题