跳到主要内容
版本:26.x.

ES6类模拟

Jest可以用来模拟导入到想要测试的文件中的ES6类。

ES6类是带有一些语法糖的构造函数。因此,ES6类的任何模拟必须是一个函数或实际的ES6类(同样是另一个函数)。所以你可以用模拟功能

一个ES6类示例

我们将使用播放声音文件的类的创新示例,SoundPlayer.以及使用该类的消费类,SoundPlayerConsumer。我们将模拟SoundPlayer.在我们的测试中SoundPlayerConsumer。我们将模拟SoundPlayer.在我们的测试中SoundPlayerConsumer

// sound-player.js
出口 默认 班级 SoundPlayer. {
构造函数 {
Foo = “酒吧” ;
}
playSoundFile 文件名 {
控制台 日志 '玩声音文件' + 文件名 ;
}
}
/ / sound-player-consumer.js
进口 SoundPlayer. './sound-player' ;
出口 默认 班级 SoundPlayerConsumer {
构造函数 {
SoundPlayer. = SoundPlayer. ;
}
playSomethingCool {
const coolSoundFileName = “song.mp3” ;
SoundPlayer. playSoundFile coolSoundFileName ;
}
}

创建ES6类模拟的4种方法

自动模拟

调用jest.mock('./ sound-player')返回一个有用的“自动模拟”,可以用来监视对类构造函数及其所有方法的调用。它用模拟构造函数替换ES6类,用模拟功能总是返回未定义的。方法调用保存在theAutomaticMock.mock.instances .methodName.mock.calls(指数)。ES6类是带有一些语法糖的构造函数。因此,ES6类的任何模拟必须是一个函数或实际的ES6类(同样是另一个函数)。所以你可以用模拟功能。方法调用保存在theAutomaticMock.mock.instances .methodName.mock.calls(指数)

请注意,如果您在课程中使用箭头函数,则会不是成为模仿的一部分。原因是箭头函数不存在于对象的原型中,它们只是持有对函数的引用的属性。原因是箭头函数不存在于对象的原型中,它们只是持有对函数的引用的属性。

如果不需要替换类的实现,这是最容易设置的选项。例如:例如:

进口 SoundPlayer. './sound-player' ;
进口 SoundPlayerConsumer './sound-player-consumer' ;
笑话 模拟 './sound-player' ; // SoundPlayer现在是模拟构造函数
摘要 = > {
//清除构造函数和方法的所有实例和调用:
SoundPlayer. mockClear ;
} ;
我们可以检查消费者是否调用了类构造函数 = > {
const soundPlayerConsumer = SoundPlayerConsumer ;
预计 SoundPlayer. toHaveBeenCalledTimes 1 ;
} ;
“我们可以检查Class实例上的消费者是否称为方法' = > {
//显示MockClear()正在工作:
预计 SoundPlayer. 不是 toHaveBeenCalled ;
const soundPlayerConsumer = SoundPlayerConsumer ;
//构造函数应该被再次调用:
预计 SoundPlayer. toHaveBeenCalledTimes 1 ;
const coolSoundFileName = “song.mp3” ;
soundPlayerConsumer playSomethingCool ;
// mock.instances可用自动模拟:
const MockSoundPlayerInstance. = SoundPlayer. 模拟 实例 [ 0. ] ;
const 模仿oundfile. = MockSoundPlayerInstance. playSoundFile ;
预计 模仿oundfile. 模拟 呼叫 [ 0. ] [ 0. ] toequal. coolSoundFileName ;
//等价于上面的检查:
预计 模仿oundfile. toHaveBeenCalledWith coolSoundFileName ;
预计 模仿oundfile. toHaveBeenCalledTimes 1 ;
} ;

手动模拟

创建一个人工模拟通过保存模拟实现__mocks__文件夹中。这允许您指定实现,并且它可以跨测试文件使用。这允许您指定实现,并且它可以跨测试文件使用。

/ / __mocks__ / sound-player.js
//将此命名导出导入到测试文件中:
出口 const 模仿oundfile. = 笑话 FN. ;
const 模拟 = 笑话 FN. mockImplementation = > {
返回 { playSoundFile 模仿oundfile. } ;
} ;
出口 默认 模拟 ;

导入所有实例共享的模拟和模拟方法:

// sound-player-configer.test.js
进口 SoundPlayer. { 模仿oundfile. } './sound-player' ;
进口 SoundPlayerConsumer './sound-player-consumer' ;
笑话 模拟 './sound-player' ; // SoundPlayer现在是模拟构造函数
摘要 = > {
//清除构造函数和方法的所有实例和调用:
SoundPlayer. mockClear ;
模仿oundfile. mockClear ;
} ;
我们可以检查消费者是否调用了类构造函数 = > {
const soundPlayerConsumer = SoundPlayerConsumer ;
预计 SoundPlayer. toHaveBeenCalledTimes 1 ;
} ;
“我们可以检查Class实例上的消费者是否称为方法' = > {
const soundPlayerConsumer = SoundPlayerConsumer ;
const coolSoundFileName = “song.mp3” ;
soundPlayerConsumer playSomethingCool ;
预计 模仿oundfile. toHaveBeenCalledWith coolSoundFileName ;
} ;

调用jest.mock ()使用模块工厂参数

jest.mock(path, moduleFactory)需要一个模块工厂争论。模块工厂是一个返回模拟的函数。模块工厂是一个返回模拟的函数。

为了模拟构造函数,模块出厂必须返回构造函数。换句话说,模块工厂必须是返回函数的函数 - 一个高阶函数(HOF)。换句话说,模块工厂必须是返回函数的函数 - 一个高阶函数(HOF)。

进口 SoundPlayer. './sound-player' ;
const 模仿oundfile. = 笑话 FN. ;
笑话 模拟 './sound-player' = > {
返回 笑话 FN. mockImplementation = > {
返回 { playSoundFile 模仿oundfile. } ;
} ;
} ;

工厂参数的一个限制是,since调用jest.mock ()已挂起到文件的顶部,因此不可能首先定义变量,然后在工厂使用它。对于从“Mock”一词开始的变量进行了例外。由您来保证他们将按时初始化!例如,由于在变量声明中使用“假”而不是“Mock”而不是“Mock”,以下内容将抛出范围错误的错误:从“Mock”一词开始的变量进行异常。由您来保证他们将按时初始化!例如,由于使用“假”而不是“变量声明”而不是“Mock”,以下将抛出范围超出错误:

//注意:这将失败
进口 SoundPlayer. './sound-player' ;
const fakeplysoundfile. = 笑话 FN. ;
笑话 模拟 './sound-player' = > {
返回 笑话 FN. mockImplementation = > {
返回 { playSoundFile fakeplysoundfile. } ;
} ;
} ;

使用以下代码替换mock模仿()模仿once()

通过调用,可以替换上述所有模拟以更改单个测试或所有测试的实现模仿()在现有的模拟上。

呼叫jest.mock被提升到代码的顶部。呼叫jest.mock被提升到代码的顶部。您可以在稍后指定模拟,例如:在beforeAll (),通过致电模仿()(或者模仿once()),而不是使用factory参数。如果需要,这还允许您更改测试之间的模拟:如果需要,这还允许您更改测试之间的模拟:

进口 SoundPlayer. './sound-player' ;
进口 SoundPlayerConsumer './sound-player-consumer' ;
笑话 模拟 './sound-player' ;
描述 '当SoundPlayer抛出错误时' = > {
Beforeall. = > {
SoundPlayer. mockImplementation = > {
返回 {
playSoundFile = > {
错误 测试错误的 ;
}
} ;
} ;
} ;
'调用playSomethingCool时应该抛出错误' = > {
const soundPlayerConsumer = SoundPlayerConsumer ;
预计 = > soundPlayerConsumer playSomethingCool toThrow ;
} ;
} ;

深入:了解模拟构造函数函数

构建构造函数函数mock使用.mockImplementation jest.fn () ()让模仿看起来比他们真的更复杂。本节展示了如何创建自己的模型,以说明如何嘲弄作品。本节展示了如何创建自己的模型,以说明如何嘲弄作品。

手动模拟是另一个ES6类

类中的被模拟类使用相同的文件名来定义ES6类__mocks__文件夹,它将作为模拟。此类将用于代替真实类。这允许您对类注入测试实现,但不提供呼叫中谍的方法。此类将用于代替真实类。这允许您对类注入测试实现,但不提供呼叫中谍的方法。

对于虚构的示例,模拟可能如下所示:

/ / __mocks__ / sound-player.js
出口 默认 班级 SoundPlayer. {
构造函数 {
控制台 日志 '模拟SoundPlayer:构造函数被称为' ;
}
playSoundFile {
控制台 日志 " Mock SoundPlayer: playSoundFile was called " ;
}
}

使用模块工厂参数进行模拟

模块出厂功能传递给jest.mock(path, moduleFactory)可以是返回函数*的HOF。这将允许调用在模拟上。同样,这允许您注入不同的测试行为进行测试,但不提供侦听呼叫的方法。

*模块工厂函数必须返回一个函数

为了模拟构造函数,模块出厂必须返回构造函数。换句话说,模块工厂必须是返回函数的函数 - 一个高阶函数(HOF)。换句话说,模块工厂必须是返回函数的函数 - 一个高阶函数(HOF)。

笑话 模拟 './sound-player' = > {
返回 函数 {
返回 { playSoundFile = > { } } ;
} ;
} ;

注意:箭头函数不起作用

注意,mock不能是一个箭头函数,因为调用在JavaScript中,箭头函数是不允许的。所以这个不行,这个不行

笑话 模拟 './sound-player' = > {
返回 = > {
//不起作用;箭头函数无法用新的函数调用
返回 { playSoundFile = > { } } ;
} ;
} ;

这将把typeerror:_soundplayer2.default不是构造函数,除非代码被转移到ES5,例如ES5。经过@ Babel / Preset-Env。(ES5没有箭头函数也没有类,所以两者都将被转发给普通功能。)(ES5没有箭头函数也没有类,所以两者都将被转发给普通功能。)

跟踪用法轨道(在模拟中窥探)

注入一个测试实现是有帮助的,但是您可能还想测试类构造函数和方法是否使用亚搏取款正确的参数调用。

窥探构造函数

为了跟踪对构造函数的调用,替换Hof返回的函数与Jest模拟功能。创建它jest.fn (),然后用模仿()。创建它jest.fn (),然后用模仿()

进口 SoundPlayer. './sound-player' ;
笑话 模拟 './sound-player' = > {
//检查构造函数调用
返回 笑话 FN. mockImplementation = > {
返回 { playSoundFile = > { } } ;
} ;
} ;

这将让我们在使用时检查我们的嘲弄类的用法soundplayer.mock.calls.期待(SoundPlayer).tohavebeencalled();或接近等同的:期望(SoundPlayer.mock.calls.length) .toEqual (1);

嘲笑非默认类出口

如果这个类不是然后,您需要返回一个与类导出名称相同的键的对象。

进口 { SoundPlayer. } './sound-player' ;
笑话 模拟 './sound-player' = > {
//检查构造函数调用
返回 {
SoundPlayer. 笑话 FN. mockImplementation = > {
返回 { playSoundFile = > { } } ;
}
} ;
} ;

监视我们班的方法

我们的嘲弄课程需要提供任何成员职能(playSoundFile在该示例中)将在我们的测试期间调用,否则我们将获得错误调用不存在的函数的错误。但我们可能希望在对这些方法的呼叫中窥探,以确保它们被预期的参数调用。但我们可能希望在对这些方法的呼叫中窥探,以确保它们被预期的参数调用。

每次测试期间调用Mock构造函数函数时,将创建一个新对象。每次测试期间调用Mock构造函数函数时,将创建一个新对象。我们填充了所有这些对象的方法调用playSoundFile使用另一个模拟函数,并在我们的测试文件中存储对该模拟函数的引用,以便在测试期间可用。

进口 SoundPlayer. './sound-player' ;
const 模仿oundfile. = 笑话 FN. ;
笑话 模拟 './sound-player' = > {
返回 笑话 FN. mockImplementation = > {
返回 { playSoundFile 模仿oundfile. } ;
//现在我们可以跟踪playSoundFile的调用
} ;
} ;

与此类似的手动模拟是:

/ / __mocks__ / sound-player.js
//将这个命名的导出导入到您的测试文件
出口 const 模仿oundfile. = 笑话 FN. ;
const 模拟 = 笑话 FN. mockImplementation = > {
返回 { playSoundFile 模仿oundfile. } ;
} ;
出口 默认 模拟 ;

用法与模块出厂函数类似,除了您可以从中省略第二个参数jest.mock (),并且您必须将所模拟的方法导入到您的测试文件中,因为它不再在那里定义了。使用原来的模块路径;不包括__mocks__。使用原来的模块路径;不包括__mocks__

在测试之间清理

要清除调用模拟构造函数及其方法的呼叫记录,我们调用MockClear()前提()功能:

摘要 = > {
SoundPlayer. mockClear ;
模仿oundfile. mockClear ;
} ;

完整的示例

下面是一个完整的测试文件,它使用模块工厂参数来jest.mock

// sound-player-configer.test.js
进口 SoundPlayer. './sound-player' ;
进口 SoundPlayerConsumer './sound-player-consumer' ;
const 模仿oundfile. = 笑话 FN. ;
笑话 模拟 './sound-player' = > {
返回 笑话 FN. mockImplementation = > {
返回 { playSoundFile 模仿oundfile. } ;
} ;
} ;
摘要 = > {
SoundPlayer. mockClear ;
模仿oundfile. mockClear ;
} ;
消费者应该能够在SoundPlayer上调用new() = > {
const soundPlayerConsumer = SoundPlayerConsumer ;
//确保构造函数创建对象:
预计 soundPlayerConsumer Tobeththy. ;
} ;
我们可以检查消费者是否调用了类构造函数 = > {
const soundPlayerConsumer = SoundPlayerConsumer ;
预计 SoundPlayer. toHaveBeenCalledTimes 1 ;
} ;
“我们可以检查Class实例上的消费者是否称为方法' = > {
const soundPlayerConsumer = SoundPlayerConsumer ;
const coolSoundFileName = “song.mp3” ;
soundPlayerConsumer playSomethingCool ;
预计 模仿oundfile. 模拟 呼叫 [ 0. ] [ 0. ] toequal. coolSoundFileName ;
} ;