跳到主要内容
版本:27.0

模拟函数

模拟函数允许您通过擦除函数的实际实现、捕获对函数的调用(以及在这些调用中传递的参数)、使用新的,并允许对返回值进行测试时配置。

模拟函数有两种方法:创建一个模拟函数以在测试代码中使用,或者编写一个手动模拟重写模块依赖项。

使用模拟函数#

假设我们正在测试一个函数的实现forEach公司,它为提供的数组中的每个项调用回调。

功能 forEach公司 ( 项目 , 回拨 ) {
对于 ( 指数 = 0 ; 指数 < 项目 . 长度 ; 指数 ++ ) {
回拨 ( 项目 [ 指数 ] ) ;
}
}

为了测试这个函数,我们可以使用一个mock函数,并检查mock的状态,以确保按预期调用回调。

常数 模拟回调 = 开玩笑 . fn公司 ( => 42 + ) ;
forEach公司 ( [ 0 , 1. ] , 模拟回调 ) ;
//mock函数被调用两次
期待 ( 模拟回调 . 嘲弄 . 电话 . 长度 ) . 托比 ( 2. ) ;
//函数的第一个调用的第一个参数是0
期待 ( 模拟回调 . 嘲弄 . 电话 [ 0 ] [ 0 ] ) . 托比 ( 0 ) ;
//第二次调用函数的第一个参数是1
期待 ( 模拟回调 . 嘲弄 . 电话 [ 1. ] [ 0 ] ) . 托比 ( 1. ) ;
//第一次调用函数的返回值是42
期待 ( 模拟回调 . 嘲弄 . 结果 [ 0 ] . 价值 ) . 托比 ( 42 ) ;

.模拟财产#

所有模拟函数都有这个特殊的.模拟属性,该属性保存有关如何调用函数以及函数返回内容的数据。这个.模拟属性还跟踪对于每个调用,也可以检查:

常数 myMock公司 = 开玩笑 . fn公司 ( ) ;
常数 A. = 新的 myMock公司 ( ) ;
常数 B = { } ;
常数 跳跃 = myMock公司 . 绑定 ( B ) ;
跳跃 ( ) ;
慰问 . 日志 ( myMock公司 . 嘲弄 . 实例 ) ;
//>[]

这些模拟成员在测试中非常有用,可以断言这些函数如何被调用、实例化或返回什么:

//函数只被调用了一次
期待 ( SomeMock函数 . 嘲弄 . 电话 . 长度 ) . 托比 ( 1. ) ;
//函数的第一个调用的第一个参数是“first arg”
期待 ( SomeMock函数 . 嘲弄 . 电话 [ 0 ] [ 0 ] ) . 托比 ( '第一个参数' ) ;
//第一次调用函数的第二个参数是'second arg'
期待 ( SomeMock函数 . 嘲弄 . 电话 [ 0 ] [ 1. ] ) . 托比 ( '第二个参数' ) ;
//第一次调用函数的返回值是“return value”
期待 ( SomeMock函数 . 嘲弄 . 结果 [ 0 ] . 价值 ) . 托比 ( '返回值' ) ;
//这个函数被实例化了两次
期待 ( SomeMock函数 . 嘲弄 . 实例 . 长度 ) . 托比 ( 2. ) ;
//此函数的第一个实例化返回的对象
//具有“name”属性,其值设置为“test”
期待 ( SomeMock函数 . 嘲弄 . 实例 [ 0 ] . 名称 ) . 托夸尔 ( '测试' ) ;

模拟返回值#

模拟函数还可用于在测试期间将测试值注入到代码中:

常数 myMock公司 = 开玩笑 . fn公司 ( ) ;
慰问 . 日志 ( myMock公司 ( ) ) ;
//>未定义
myMock公司 . 模拟返回值一次 ( 10 ) . 模拟返回值一次 ( “x” ) . 模拟返回值 ( 是的 ) ;
慰问 . 日志 ( myMock公司 ( ) , myMock公司 ( ) , myMock公司 ( ) , myMock公司 ( ) ) ;
//>10,'x',对,对

模拟函数在使用函数延续传递样式的代码中也非常有效。以这种风格编写的代码有助于避免需要复杂的存根来重新创建它们所代表的真实组件的行为,从而有利于在使用之前将值直接注入测试。亚搏取款

常数 过滤器测试fn = 开玩笑 . fn公司 ( ) ;
//对第一个调用进行模拟返回“true”,
//第二个电话是“假”
过滤器测试fn . 模拟返回值一次 ( 是的 ) . 模拟返回值一次 ( ) ;
常数 结果 = [ 11 , 12 ] . 滤波器 ( 号码 => 过滤器测试fn ( 号码 ) ) ;
慰问 . 日志 ( 结果 ) ;
// > [11]
慰问 . 日志 ( 过滤器测试fn . 嘲弄 . 电话 [ 0 ] [ 0 ] ) ; // 11
慰问 . 日志 ( 过滤器测试fn . 嘲弄 . 电话 [ 1. ] [ 0 ] ) ; // 12

大多数真实世界的例子实际上涉及到在依赖组件上获得模拟函数并对其进行配置,但技术是相同的。在这些情况下,尽量避免在任何未被直接测试的函数中实现逻辑的诱惑。

模拟模块#

假设我们有一个类从API中获取用户。该类使用轴心调用API,然后返回数据包含所有用户的属性:

//用户.js
进口 轴心 “axios” ;
用户 {
静止的 全部的 ( ) {
返回 轴心 . 得到 ( '/users.json' ) . 然后 ( 责任 => 责任 . 数据 ) ;
}
}
出口 违约 用户 ;

现在,为了测试这个方法而不实际地命中API(从而创建缓慢而脆弱的测试),我们可以使用开玩笑。嘲笑(…)函数自动模拟axios模块。

一旦我们模拟了这个模块,我们就可以提供一个mockResolvedValue对于。获取它返回我们希望测试针对的数据。实际上,我们是在说我们想要axios.get('/users.json')以假回应。

//用户.测试.js
进口 轴心 “axios” ;
进口 用户 './用户' ;
开玩笑 . 嘲弄 ( “axios” ) ;
测试 ( '应获取用户' , ( ) => {
常数 用户 = [ { 名称 : “鲍勃” } ] ;
常数 责任 = { 数据 : 用户 } ;
轴心 . 得到 . mockResolvedValue ( 责任 ) ;
//或者,根据您的用例,您可以使用以下选项:
//axios.get.mockImplementation(()=>Promise.resolve(resp))
返回 用户 . 全部的 ( ) . 然后 ( 数据 => 期待 ( 数据 ) . 托夸尔 ( 用户 ) ) ;
} ) ;

模拟实现#

不过,在某些情况下,除了指定返回值和完全替换模拟函数的实现之外,还可以使用其他方法。这可以通过开玩笑.fn或者模拟实现一次方法。

常数 myMockFn公司 = 开玩笑 . fn公司 ( 断路器 => 断路器 ( 无效的 , 是的 ) ) ;
myMockFn公司 ( ( , 瓦尔 ) => 慰问 . 日志 ( 瓦尔 ) ) ;
//>正确

这个模拟实现方法在需要定义从另一个模块创建的模拟函数的默认实现时非常有用:

//foo.js公司
模块 . 出口 = 功能 ( ) {
//一些实施;
} ;
//测试.js
开玩笑 . 嘲弄 ( '../foo' ) ; //自动模拟会自动发生这种情况
常数 = 要求 ( '../foo' ) ;
//foo是一个模拟函数
. 模拟实现 ( ( ) => 42 ) ;
( ) ;
// > 42

当您需要重新创建模拟函数的复杂行为,以便多个函数调用产生不同的结果时,请使用模拟实现一次方法:

常数 myMockFn公司 = 开玩笑
. fn公司 ( )
. 模拟实现一次 ( 断路器 => 断路器 ( 无效的 , 是的 ) )
. 模拟实现一次 ( 断路器 => 断路器 ( 无效的 , ) ) ;
myMockFn公司 ( ( , 瓦尔 ) => 慰问 . 日志 ( 瓦尔 ) ) ;
//>正确
myMockFn公司 ( ( , 瓦尔 ) => 慰问 . 日志 ( 瓦尔 ) ) ;
//>错误

当模拟函数用完用定义的实现时模拟实现一次,它将使用开玩笑.fn(如有定义):

常数 myMockFn公司 = 开玩笑
. fn公司 ( ( ) => '默认' )
. 模拟实现一次 ( ( ) => '第一次呼叫' )
. 模拟实现一次 ( ( ) => '第二次呼叫' ) ;
慰问 . 日志 ( myMockFn公司 ( ) , myMockFn公司 ( ) , myMockFn公司 ( ) , myMockFn公司 ( ) ) ;
//>'第一次呼叫','第二次呼叫','默认','默认'

对于我们的方法通常是链式的(因此总是需要返回),我们有一个糖类API以.mockReturnThis()同时位于所有mock上的函数:

常数 迈奥比 = {
我的方法 : 开玩笑 . fn公司 ( ) . 模仿这个 ( ) ,
} ;
//与相同
常数 其他对象 = {
我的方法 : 开玩笑 . fn公司 ( 功能 ( ) {
返回 ;
} ) ,
} ;

假名字#

您可以选择提供模拟函数的名称,该名称将显示在测试错误输出中,而不是“jest.fn()”。如果希望能够快速识别在测试输出中报告错误的模拟函数,请使用此选项。

常数 myMockFn公司 = 开玩笑
. fn公司 ( )
. 模拟返回值 ( '默认' )
. 模拟实现 ( 标量 => 42 + 标量 )
. 模拟名称 ( '添加42' ) ;

自定义匹配器#

最后,为了减少声明如何调用模拟函数的要求,我们为您添加了一些自定义匹配器函数:

//模拟函数至少被调用了一次
期待 ( 模拟函数 ) . 已被调用 ( ) ;
//使用指定的参数至少调用了一次模拟函数
期待 ( 模拟函数 ) . 与…共事 ( arg1 , arg2 ) ;
//对模拟函数的最后一次调用是用指定的参数调用的
期待 ( 模拟函数 ) . 最后被称为 ( arg1 , arg2 ) ;
//所有调用和mock的名称都写为快照
期待 ( 模拟函数 ) . 托马斯快照 ( ) ;

这些匹配器是普通形式的糖,用于检查.模拟财产。如果这更符合您的口味或者您需要做一些更具体的事情,您可以自己手动完成:

//模拟函数至少被调用了一次
期待 ( 模拟函数 . 嘲弄 . 电话 . 长度 ) . ( 0 ) ;
//使用指定的参数至少调用了一次模拟函数
期待 ( 模拟函数 . 嘲弄 . 电话 ) . 包含相等 ( [ arg1 , arg2 ] ) ;
//对模拟函数的最后一次调用是用指定的参数调用的
期待 ( 模拟函数 . 嘲弄 . 电话 [ 模拟函数 . 嘲弄 . 电话 . 长度 - 1. ] ) . 托夸尔 ( [
arg1 ,
arg2 ,
] ) ;
//最后一次调用mock函数的第一个参数是'42'`
//(注意,对于这个特定的断言没有sugar助手)亚搏取款
期待 ( 模拟函数 . 嘲弄 . 电话 [ 模拟函数 . 嘲弄 . 电话 . 长度 - 1. ] [ 0 ] ) . 托比 ( 42 ) ;
//快照将检查模拟是否被调用了相同的次数,
//同样的顺序,同样的论点。它还将在名称上声明。
期待 ( 模拟函数 . 嘲弄 . 电话 ) . 托夸尔 ( [ [ arg1 , arg2 ] ] ) ;
期待 ( 模拟函数 . 获取mockname ( ) ) . 托比 ( '假名字' ) ;

要获得匹配者的完整列表,请查看参考文件yabo2013.

上次更新时间通过塞巴斯蒂安·阿伯拉斯特里