模拟ES6类导入
我想在我的测试文件中模拟我的ES6类导入。
如果被模拟的类有多个使用者,那么将模拟移到__mocks__中可能是有意义的,这样所有的测试都可以共享模拟,但是在此之前,我希望将模拟保留在测试文件中。
笑话模拟()
jest.mock()
可以模拟导入的模块。当传递单个参数时:
jest.mock('./my-class.js');
它使用在与mock文件相邻的__mocks__文件夹中找到的mock实现,或者创建自动mock。
模块工厂参数
jest.mock()
接受第二个参数,这是一个模块工厂函数。***对于使用export default
导出的ES6类,不清楚此工厂函数应返回什么。是否:
1.另一个返回模拟类示例的对象的函数?
1.模拟类示例的对象?
1.一个具有default
属性的对象,该属性是一个返回模拟类示例的对象的函数?
1.一个函数返回一个本身返回1、2或3的高阶函数?
文件很含糊:
第二个参数可以用来指定一个正在运行的显式模块工厂,而不是使用Jest的自动锁功能:
当消费者import
是类时,我很难找到一个工厂定义,它可以充当构造函数,我一直在得到TypeError: _soundPlayer2.default is not a constructor
(例如)。
我尝试过避免使用箭头函数(因为它们不能用new
调用),并让工厂返回一个具有default
属性(或不具有)的对象。
这里有一个例子。这行不通;所有测试都抛出TypeError: _soundPlayer2.default is not a constructor
。
正在测试的类: * 声音播放器-消费者. 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);
}
}
被嘲笑的类:* * 声音播放器. js**
export default class SoundPlayer {
constructor() {
// Stub
this.whatever = 'whatever';
}
playSoundFile(fileName) {
// Stub
console.log('Playing sound file ' + fileName);
}
}
测试文件:* * 声音播放器-消费者.测试. 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);
});
我可以把什么作为第二个参数传递给jest.mock(),以允许示例中的所有测试都通过?如果测试需要修改,也没关系-只要它们仍然测试相同的东西。
5条答案
按热度按时间deikduxw1#
工厂函数必须返回函数
工厂函数必须返回模拟:替代它所嘲笑的东西的物体。
因为我们模拟的是一个ES6类,也就是a function with some syntactic sugar,那么这个模拟本身必须是一个函数,因此传递给
jest.mock()
的工厂函数必须返回一个函数;换句话说,它必须是高阶函数。在上面的代码中,factory函数返回一个对象,由于在该对象上调用
new
失败,所以它不起作用。可以调用
new
的简单模拟:下面是一个简单的版本,因为它返回一个函数,所以允许调用
new
:注意:箭头功能无效
注意,我们的mock不能是一个箭头函数,因为我们不能在Javascript中对箭头函数调用new;这是语言固有的特性。所以这是行不通的
这将引发***类型错误:_soundPlayer2.default不是构造函数***。
跟踪使用情况(监视模拟)
不抛出错误固然很好,但我们可能需要测试构造函数是否使用正确的参数调用。
为了跟踪对构造函数的调用,我们可以用Jest mock函数替换霍夫返回的函数,用
jest.fn()
创建它,然后用mockImplementation()
指定它的实现。这将允许我们使用
SoundPlayer.mock.calls
检查模拟类的使用情况。监视我们类的方法
模拟类需要提供在测试过程中调用的所有成员函数(示例中为
playSoundFile
),否则调用不存在的函数时会出错,但我们可能还需要监视对这些方法的调用,以确保它们是使用预期参数调用的。因为在测试过程中会创建一个新的模拟对象,所以
SoundPlayer.playSoundFile.calls
对我们没有帮助。为了解决这个问题,我们用另一个模拟函数填充playSoundFile
,并在测试文件中存储对同一个模拟函数的引用,这样我们就可以在测试过程中访问它。完整示例
下面是它在测试文件中的外观:
68bkxrlz2#
如果您仍然在使用
TypeError: ...default is not a constructor
并且正在使用TypeScript,请继续阅读。TypeScript正在翻译您的ts文件,并且您的模块可能是使用ES2015s import.
const soundPlayer = require('./sound-player')
导入的。因此,创建作为默认值导出的类的示例将如下所示:但是,如果您按照文档中的建议模拟类。你会得到同样的错误,因为
soundPlayer.default
没有指向函数。你的模拟必须返回一个对象,该对象有一个指向函数的默认属性。对于命名导入(如
import { OAuth2 } from './oauth'
),请将default
替换为导入的模块名称,在此示例中为OAuth2
:6jjcrrmo3#
Stone和Santiago帮我完成了这个任务,我只想提一下,另外,我必须在import语句之前添加jest mock函数,如下所示:
谢谢你的帮助!
a7qyws3x4#
如果您已经定义了一个模拟类,您可以使用类似于:
代码将执行类似于用MockedRealClass的方法和属性定义替换原始RealClass的方法和属性定义的操作。
mfuanj7w5#
这对我很有效:
然后在测试中,使用
jest.fn().mockImplementation(()
返回SoundPlayer
作为键对象