跳转到主要内容
版本:26.倍

模拟功能

模拟函数允许您通过删除函数的实际实现、捕获对函数的调用(以及在这些调用中传递的参数)、捕获用实例化时的构造函数实例来测试代码之间的链接,并允许在测试时配置返回值。

有两种方法来模拟函数:通过创建一个在测试代码中使用的模拟函数,或者编写一个<一个href="//www.ieatrice.com/docs/manual-mocks">人工模拟重写模块依赖项。

使用模拟函数<一个类="hash-link" href="#using-a-mock-function" title="#">#

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

函数 forEach ( 项目 , 回调 ) {
( 指数 = 0 ; 指数 < 项目 长度 ; 指数 ++ ) {
回调 ( 项目 ( 指数 ] ) ;
}
}

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

常量 mockCallback = 开玩笑 fn ( x = > 42 + x ) ;
forEach ( ( 0 , 1 ] , mockCallback ) ;
//模拟函数被调用两次
预计 ( mockCallback 模拟 调用 长度 ) 托比 ( 2 ) ;
//函数第一次调用的第一个参数为0
预计 ( mockCallback 模拟 调用 ( 0 ] ( 0 ] ) 托比 ( 0 ) ;
//第二个函数调用的第一个参数是1
预计 ( mockCallback 模拟 调用 ( 1 ] ( 0 ] ) 托比 ( 1 ) ;
//第一次调用函数的返回值是42
预计 ( mockCallback 模拟 结果 ( 0 ] 价值 ) 托比 ( 42 ) ;

.mock财产<一个类="hash-link" href="#mock-property" title="#">#

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

常量 myMock = 开玩笑 fn ( ) ;
常量 一个 = myMock ( ) ;
常量 b = { } ;
常量 绑定 = myMock 绑定 ( b ) ;
绑定 ( ) ;
控制台 日志 ( myMock 模拟 实例 ) ;
// > []

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

//函数被精确地调用一次
预计 ( someMockFunction 模拟 调用 长度 ) 托比 ( 1 ) ;
//第一次调用函数的第一个参数是'first arg'
预计 ( someMockFunction 模拟 调用 ( 0 ] ( 0 ] ) 托比 ( 的第一个参数 ) ;
//第一次调用函数的第二个参数是'second arg'
预计 ( someMockFunction 模拟 调用 ( 0 ] ( 1 ] ) 托比 ( 第二个参数的 ) ;
//第一次调用函数的返回值是'return value'
预计 ( someMockFunction 模拟 结果 ( 0 ] 价值 ) 托比 ( “返回值” ) ;
//这个函数被精确地实例化两次
预计 ( someMockFunction 模拟 实例 长度 ) 托比 ( 2 ) ;
//该函数的第一个实例化返回的对象
//有一个' name '属性,它的值被设置为'test'
预计 ( someMockFunction 模拟 实例 ( 0 ] 的名字 ) toEqual ( “测试” ) ;

模拟返回值<一个类="hash-link" href="#mock-return-values" title="#">#

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

常量 myMock = 开玩笑 fn ( ) ;
控制台 日志 ( myMock ( ) ) ;
/ / >定义
myMock mockReturnValueOnce ( 10 ) mockReturnValueOnce ( “x” ) mockReturnValue ( 真正的 ) ;
控制台 日志 ( myMock ( ) , myMock ( ) , myMock ( ) , myMock ( ) ) ;
// 10, 'x', true, true

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

常量 filterTestFn = 开玩笑 fn ( ) ;
//为第一次调用模拟返回' true ',
//和' false '为第二个调用
filterTestFn mockReturnValueOnce ( 真正的 ) mockReturnValueOnce ( ) ;
常量 结果 = ( 11 , 12 ] 过滤器 ( 全国矿工工会 = > filterTestFn ( 全国矿工工会 ) ) ;
控制台 日志 ( 结果 ) ;
/ / > [11]
控制台 日志 ( filterTestFn 模拟 调用 ) ;
// > [[11], [12]]

大多数现实世界的示例实际上都涉及到在依赖组件上获取模拟函数并进行配置,但技术是相同的。在这些情况下,尽量避免在没有直接测试的函数中实现逻辑。

模拟模块<一个类="hash-link" href="#mocking-modules" title="#">#

假设我们有一个从API获取用户的类。这个类使用<一个href="https://github.com/axios/axios" target="_blank" rel="noopener noreferrer">axios调用API,然后返回数据属性包含所有用户:

/ / users.js
进口 axios “axios” ;
用户 {
静态 所有 ( ) {
返回 axios 得到 ( ' / users.json ' ) 然后 ( 分别地 = > 分别地 数据 ) ;
}
}
出口 默认的 用户 ;

现在,为了测试这个方法而不实际碰到API(从而创建缓慢而脆弱的测试),我们可以使用jest.mock(…)函数自动模拟axios模块。

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

/ / users.test.js
进口 axios “axios” ;
进口 用户 ”。/用户的 ;
开玩笑 模拟 ( “axios” ) ;
测试 ( “应该获取用户的 , ( ) = > {
常量 用户 = ( { 的名字 : “鲍勃” } ] ;
常量 分别地 = { 数据 : 用户 } ;
axios 得到 mockResolvedValue ( 分别地 ) ;
//根据你的用例,你可以使用以下方法:
// axios.get.mockImplementation(() => Promise.resolve(resp))
返回 用户 所有 ( ) 然后 ( 数据 = > 预计 ( 数据 ) toEqual ( 用户 ) ) ;
} ) ;

模拟实现<一个类="hash-link" href="#mock-implementations" title="#">#

不过,在某些情况下,除了能够指定返回值和完全替换模拟函数的实现之外,它还很有用。这可以用jest.fn或者是mockImplementationOnce方法的模拟函数。

常量 myMockFn = 开玩笑 fn ( cb = > cb ( , 真正的 ) ) ;
myMockFn ( ( 犯错 , 瓦尔 ) = > 控制台 日志 ( 瓦尔 ) ) ;
/ / >真

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

/ / foo.js
模块 出口 = 函数 ( ) {
/ /一些实现;
} ;
/ / . js
开玩笑 模拟 ( “. . / foo” ) ; //通过automocking自动实现
常量 喷火 = 需要 ( “. . / foo” ) ;
// foo是一个mock函数
喷火 mockImplementation ( ( ) = > 42 ) ;
喷火 ( ) ;
/ / > 42

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

常量 myMockFn = 开玩笑
fn ( )
mockImplementationOnce ( cb = > cb ( , 真正的 ) )
mockImplementationOnce ( cb = > cb ( , ) ) ;
myMockFn ( ( 犯错 , 瓦尔 ) = > 控制台 日志 ( 瓦尔 ) ) ;
/ / >真
myMockFn ( ( 犯错 , 瓦尔 ) = > 控制台 日志 ( 瓦尔 ) ) ;
/ / >假

当被模拟的函数用完定义的实现时mockImplementationOnce,它将执行默认的实现集jest.fn(如果有定义):

常量 myMockFn = 开玩笑
fn ( ( ) = > “默认” )
mockImplementationOnce ( ( ) = > “第一个电话” )
mockImplementationOnce ( ( ) = > “第二个电话” ) ;
控制台 日志 ( myMockFn ( ) , myMockFn ( ) , myMockFn ( ) , myMockFn ( ) ) ;
// > 'first call', 'second call', 'default', 'default'

在这种情况下,我们的方法通常是链接的(因此总是需要返回)),我们有一个含糖API,以简化形式为a.mockReturnThis ()函数也位于所有模拟对象上:

常量 myObj = {
myMethod : 开玩笑 fn ( ) mockReturnThis ( ) ,
} ;
//等于
常量 otherObj = {
myMethod : 开玩笑 fn ( 函数 ( ) {
返回 ;
} ) ,
} ;

假的名字<一个类="hash-link" href="#mock-names" title="#">#

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

常量 myMockFn = 开玩笑
fn ( )
mockReturnValue ( “默认” )
mockImplementation ( 标量 = > 42 + 标量 )
mockName ( “add42” ) ;

定制的匹配器<一个类="hash-link" href="#custom-matchers" title="#">#

最后,为了降低断言mock函数是如何被调用的要求,我们为你添加了一些自定义匹配器函数:

// mock函数至少被调用一次
预计 ( mockFunc ) toHaveBeenCalled ( ) ;
//使用指定的参数至少调用一次模拟函数
预计 ( mockFunc ) toHaveBeenCalledWith ( __arg1 , 最长 ) ;
//最后一次调用模拟函数是使用指定的参数调用的
预计 ( mockFunc ) toHaveBeenLastCalledWith ( __arg1 , 最长 ) ;
//所有调用和mock的名称都被写入快照
预计 ( mockFunc ) toMatchSnapshot ( ) ;

这些匹配器是常用的检查形式的糖.mock财产。如果这更符合你的口味,或者你需要做一些更具体的事情,你可以自己手动做:

// mock函数至少被调用一次
预计 ( mockFunc 模拟 调用 长度 ) toBeGreaterThan ( 0 ) ;
//使用指定的参数至少调用一次模拟函数
预计 ( mockFunc 模拟 调用 ) toContainEqual ( ( __arg1 , 最长 ] ) ;
//最后一次调用模拟函数是使用指定的参数调用的
预计 ( mockFunc 模拟 调用 ( mockFunc 模拟 调用 长度 - 1 ] ) toEqual ( (
__arg1 ,
最长 ,
] ) ;
//最后一次调用mock函数的第一个参数是“42”
//(注意,对于这个特定的断言没有糖助手)亚搏取款
预计 ( mockFunc 模拟 调用 ( mockFunc 模拟 调用 长度 - 1 ] ( 0 ] ) 托比 ( 42 ) ;
//一个快照将检查一个mock被调用的次数是否相同,
//以相同的顺序,使用相同的参数。它还将对名称进行断言。
预计 ( mockFunc 模拟 调用 ) toEqual ( ( ( __arg1 , 最长 ] ] ) ;
预计 ( mockFunc getMockName ( ) ) 托比 ( “一个模拟的名字” ) ;

有关匹配器的完整列表,请查看<一个href="/docs/expect">参考文档yabo2013。

最后一次更新在通过Sebastien Lorber