跳过主要内容
版本:27.2

模拟功能

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

有两种方法可以模拟函数:创建一个模拟函数在测试代码中使用,或者编写一个<一个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
// mock函数被调用两次
预计 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="#">#

Mock函数也可以用于在测试期间向代码中注入测试值:

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

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

常量 filterTestFn 开玩笑 fn
//让mock函数在第一次调用时返回true,
//和' false '为第二个调用
filterTestFn mockReturnValueOnce 真正的 mockReturnValueOnce
常量 结果 11 12 过滤器 全国矿工工会 => filterTestFn 全国矿工工会
控制台 日志 结果
/ / > [11]
控制台 日志 filterTestFn 模拟 调用 0 0 / / 11
控制台 日志 filterTestFn 模拟 调用 1 0 / / 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(respp))
返回 用户 所有 然后 数据 => 预计 数据 toEqual 用户

嘲笑泛音<一个类="hash-link" href="#mocking-partials" title="#">#

模块的子集可以被mock,模块的其余部分可以保留它们的实际实现:

/ / foo-bar-baz.js
出口 常量 喷火 “foo”
出口 常量 酒吧 => “酒吧”
出口 默认的 => “记者”
/ / . js
进口 defaultExport 酒吧 喷火 “. . / foo-bar-baz”
开玩笑 模拟 “. . / foo-bar-baz” =>
常量 originalModule 开玩笑 requireActual “. . / foo-bar-baz”
//模拟默认的导出和命名为export 'foo'
返回
__esModule 真正的
... originalModule
默认的 开玩笑 fn => “嘲笑巴兹”
喷火 “嘲笑foo”
测试 “应该做部分模仿” =>
常量 defaultExportResult defaultExport
预计 defaultExportResult 托比 “嘲笑巴兹”
预计 defaultExport toHaveBeenCalled
预计 喷火 托比 “嘲笑foo”
预计 酒吧 托比 “酒吧”

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

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

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

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

/ / foo.js
模块 出口 函数
/ /一些实现;
/ / . js
开玩笑 模拟 “. . / foo” //在automocking中自动发生
常量 喷火 需要 “. . / foo”
foo是一个模拟函数
喷火 mockImplementation => 42
喷火
/ / > 42

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

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

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

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

对于有通常被链接的方法(因此总是需要返回)的情况),我们有一个含糖的API来简化这一点.mockReturnThis ()函数也位于所有mock上:

常量 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
//使用指定的参数至少调用mock函数一次
预计 mockFunc toHaveBeenCalledWith __arg1 最长
//最后一次调用mock函数时使用了指定的参数
预计 mockFunc toHaveBeenLastCalledWith __arg1 最长
//所有调用和mock的名称都被写入快照
预计 mockFunc toMatchSnapshot

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

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

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

最后一次更新在通过四门Bekkhus