我想在我的测试文件中模拟我的ES6类导入。
如果被模拟的类有多个使用者,将模拟移动到__mocks__可能是有意义的,这样所有测试都可以共享模拟,但在此之前我想将模拟保留在测试文件中
jest.mock()
可以模拟导入的模块。传递一个参数时:
jest.mock('./my-class.js');
它使用与模拟文件相邻的__mocks__文件夹中的模拟实现,或创建自动模拟。
jest.mock()
采用第二个参数,这是一个模块工厂功能。 对于使用export default
导出的ES6类,它不清楚此工厂函数应返回的内容。 是吗:
default
的对象是一个返回模仿该类实例的对象的函数吗?The docs非常模糊:
第二个参数可用于指定正在运行的显式模块工厂,而不是使用Jest的自动锁定功能:
我努力想出一个工厂定义,当消费者import
成为类时,它可以作为构造函数。我一直得到TypeError: _soundPlayer2.default is not a constructor
(例如)。
我试过避免使用箭头函数(因为它们不能用new
调用)并让工厂返回一个具有default
属性的对象(或不是)。
这是一个例子。这不起作用;所有测试都抛出TypeError: _soundPlayer2.default is not a constructor
。
正在测试的类: 的声玩家consumer.js
import SoundPlayer from './sound-player'; // Default import
export default class SoundPlayerConsumer {
constructor() {
this.soundPlayer = new SoundPlayer(); //TypeError: _soundPlayer2.default is not a constructor
}
playSomethingCool() {
const coolSoundFileName = 'song.mp3';
this.soundPlayer.playSoundFile(coolSoundFileName);
}
}
被嘲笑的类: 的声音player.js
export default class SoundPlayer {
constructor() {
// Stub
this.whatever = 'whatever';
}
playSoundFile(fileName) {
// Stub
console.log('Playing sound file ' + fileName);
}
}
测试文件: sound-player-consumer.test.js
import SoundPlayerConsumer from './sound-player-consumer';
import SoundPlayer from './sound-player';
// What can I pass as the second arg here that will
// allow all of the tests below to pass?
jest.mock('./sound-player', function() {
return {
default: function() {
return {
playSoundFile: jest.fn()
};
}
};
});
it('The consumer should be able to call new() on SoundPlayer', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(soundPlayerConsumer).toBeTruthy(); // Constructor ran with no errors
});
it('We can check if the consumer called the mocked class constructor', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalled();
});
it('We can check if the consumer called a method on the class instance', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
const coolSoundFileName = 'song.mp3';
soundPlayerConsumer.playSomethingCool();
expect(SoundPlayer.playSoundFile).toHaveBeenCalledWith(coolSoundFileName);
});
我可以将第二个arg传递给jest.mock(),它将允许示例中的所有测试通过?如果测试需要修改,那就好了 - 只要他们仍然测试相同的东西。
答案 0 :(得分:1)
对于阅读此问题的任何人,我都设置了GitHub repository来测试模拟模块和类。它基于上面答案中描述的原理,但涵盖了默认导出和命名导出。
答案 1 :(得分:0)
如果您仍在使用TypeError: ...default is not a constructor
并且正在使用TypeScript,请继续阅读。
TypeScript正在编译您的ts文件,您的模块很可能是使用ES2015s导入导入的。
const soundPlayer = require('./sound-player')
。
因此,创建作为默认值导出的类的实例将如下所示:
new soundPlayer.default()
。
但是,如果您按照文档中的建议嘲笑该类。
jest.mock('./sound-player', () => {
return jest.fn().mockImplementation(() => {
return { playSoundFile: mockPlaySoundFile };
});
});
由于soundPlayer.default
没有指向函数,您将得到相同的错误。
模拟必须返回一个对象,该对象的默认属性指向一个函数。
jest.mock('./sound-player', () => {
return {
default: jest.fn().mockImplementation(() => {
return {
playSoundFile: mockPlaySoundFile
}
})
}
})
答案 2 :(得分:0)
如果你定义了一个模拟类,你可以使用类似的东西:
jest.mock("../RealClass", () => {
const mockedModule = jest.requireActual(
"../path-to-mocked-class/MockedRealClass"
);
return {
...mockedModule,
};
});
代码会做一些事情,比如用 MockedRealClass 之一替换原始 RealClass 的方法和属性定义。