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

时间:2021-11-19 15:54:08

I'm beginning to think this isn't possible, but I want to ask anyway.

我开始觉得这是不可能的,但我还是想问。

I want to test that one of my ES6 modules calls another ES6 module in a particular way. With Jasmine this is super easy --

我想测试我的ES6模块中的一个调用另一个ES6模块。有了茉莉花,这就超级简单了—

The app code:

应用程序代码:

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

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

And the test code:

测试代码:

//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);
  });
});

What's the equivalent with Jest? I feel like this is such a simple thing to want to do, but I've been tearing my hair out trying to figure it out.

开玩笑的意思是什么?我觉得这是一件很简单的事情,但我一直在拼命想弄清楚。

The closest I've come is by replacing the imports with requires, and moving them inside the tests/functions. Neither of which are things I want to do.

我最接近的方法是用需求替换导入,并在测试/函数中移动它们。这两件事我都不想做。

// 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);
  });
});

For bonus points, I'd love to make the whole thing work when the function inside dependency.js is a default export. However, I know that spying on default exports doesn't work in Jasmine (or at least I could never get it to work), so I'm not holding out hope that it's possible in Jest either.

对于额外的积分,我希望在依赖项内的函数起作用时,整个东西都能工作。js是默认导出。然而,我知道对违约出口的监视在Jasmine中是不起作用的(或者至少我无法让它起作用),所以我也不希望它在开玩笑中成为可能。

4 个解决方案

#1


99  

I've been able to solve this by using a hack involving import *. It even works for both named and default exports!

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

For a named export:

一个命名的出口:

// 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);
  });
});

Or for a default export:

或默认出口:

// 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
  });
});

As Mihai Damian quite rightly pointed out below, this is mutating the module object of dependency, and so it will 'leak' across to other tests. So if you use this approach you should probably store the original value and then set it back again after each test. This is why sinon has a restore method!

正如Mihai Damian在下面非常正确地指出的那样,这正在改变依赖项的模块对象,因此它将“泄漏”到其他测试中。因此,如果您使用这种方法,您可能应该存储原始值,然后在每次测试之后重新设置它。这就是为什么sinon有一个恢复方法!

#2


68  

You have to mock the module and set the spy by yourself:

你必须模仿这个模块,自己设置间谍:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}))

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

#3


27  

Adding more to Andreas answer. I had the same problem with ES6 code but did not want to mutate the imports. That looked hacky. So I did this

给安德烈亚斯的答案增加了更多。我对ES6代码也有同样的问题,但不想改变导入。看起来出租汽车司机。所以我做了这个

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');

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

And added dependency.js in " __ mocks __" folder parallel to dependency.js. This worked for me. Also, this gave me option to return suitable data from mock implementation. Make sure you give the correct path to the module you want to mock.

并添加依赖项。js在“__ mocks __”文件夹中与依赖项并行。这为我工作。此外,这使我可以选择从mock实现返回适当的数据。确保您为要模拟的模块提供了正确的路径。

#4


20  

To mock an ES6 dependency module default export using jest:

要模拟使用jest的ES6依赖模块默认导出:

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

jest.mock('../dependency');

// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);

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

    expect(dependency).toHaveBeenCalledTimes(1);
    expect(dependency).toHaveBeenCalledWith(4);
  });
});

The other options didn't work for my case.

其他的选项对我的案子不起作用。

#1


99  

I've been able to solve this by using a hack involving import *. It even works for both named and default exports!

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

For a named export:

一个命名的出口:

// 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);
  });
});

Or for a default export:

或默认出口:

// 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
  });
});

As Mihai Damian quite rightly pointed out below, this is mutating the module object of dependency, and so it will 'leak' across to other tests. So if you use this approach you should probably store the original value and then set it back again after each test. This is why sinon has a restore method!

正如Mihai Damian在下面非常正确地指出的那样,这正在改变依赖项的模块对象,因此它将“泄漏”到其他测试中。因此,如果您使用这种方法,您可能应该存储原始值,然后在每次测试之后重新设置它。这就是为什么sinon有一个恢复方法!

#2


68  

You have to mock the module and set the spy by yourself:

你必须模仿这个模块,自己设置间谍:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}))

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

#3


27  

Adding more to Andreas answer. I had the same problem with ES6 code but did not want to mutate the imports. That looked hacky. So I did this

给安德烈亚斯的答案增加了更多。我对ES6代码也有同样的问题,但不想改变导入。看起来出租汽车司机。所以我做了这个

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');

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

And added dependency.js in " __ mocks __" folder parallel to dependency.js. This worked for me. Also, this gave me option to return suitable data from mock implementation. Make sure you give the correct path to the module you want to mock.

并添加依赖项。js在“__ mocks __”文件夹中与依赖项并行。这为我工作。此外,这使我可以选择从mock实现返回适当的数据。确保您为要模拟的模块提供了正确的路径。

#4


20  

To mock an ES6 dependency module default export using jest:

要模拟使用jest的ES6依赖模块默认导出:

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

jest.mock('../dependency');

// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);

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

    expect(dependency).toHaveBeenCalledTimes(1);
    expect(dependency).toHaveBeenCalledWith(4);
  });
});

The other options didn't work for my case.

其他的选项对我的案子不起作用。