模拟功能
模拟函数允许您通过删除函数的实际实现、捕获对函数的调用(以及在这些调用中传递的参数)、捕获用实例化时的构造函数实例来测试代码之间的链接新
,并允许在测试时配置返回值。
有两种方法来模拟函数:通过创建一个在测试代码中使用的模拟函数,或者编写一个人工模拟
重写模块依赖项。
使用模拟函数
让我们假设我们正在测试一个函数的实现forEach
,它为提供的数组中的每个项调用回调。
函数forEach(项目,回调){为(让指数=0;指数< items.length;指数+ +){回调(项目(指标));}}
要测试这个函数,我们可以使用一个模拟函数,并检查模拟的状态,以确保按预期调用回调函数。
常量mockCallback = jest.fn (x= >42+ x);forEach ([0,1), mockCallback);//模拟函数被调用两次期望(mockCallback.mock.calls.length) .toBe (2);//函数第一次调用的第一个参数为0期望(mockCallback.mock.calls [0][0]) .toBe (0);//第二个函数调用的第一个参数是1期望(mockCallback.mock.calls [1][0]) .toBe (1);//第一次调用函数的返回值是42期望(mockCallback.mock.results [0] value) .toBe (42);
.mock
财产
所有模拟函数都有这种特殊的特性.mock
属性,该属性保存关于如何调用函数和函数返回内容的数据。的.mock
属性还可以跟踪的值这
对于每个调用,因此也可以检查它:
常量myMock = jest.fn ();常量一个=新myMock ();常量b = {};常量绑定= myMock.bind (b);绑定();控制台. log (myMock.mock.instances);// > [, ]
这些模拟成员在测试中非常有用,可以断言这些函数如何被调用、实例化或它们返回什么:
//函数被精确地调用一次期望(someMockFunction.mock.calls.length) .toBe (1);//第一次调用函数的第一个参数是'first arg'期望(someMockFunction.mock.calls [0][0]) .toBe (的第一个参数);//第一次调用函数的第二个参数是'second arg'期望(someMockFunction.mock.calls [0][1]) .toBe (第二个参数的);//第一次调用函数的返回值是'return value'期望(someMockFunction.mock.results [0] value) .toBe (“返回值”);//这个函数被精确地实例化两次期望(someMockFunction.mock.instances.length) .toBe (2);//该函数的第一个实例化返回的对象//有一个' name '属性,它的值被设置为'test'期望(someMockFunction.mock.instances [0] . name) .toEqual (“测试”);
模拟返回值
模拟函数还可以在测试期间将测试值注入到代码中:
常量myMock = jest.fn ();控制台. log (myMock ());/ / >定义myMock.mockReturnValueOnce (10) .mockReturnValueOnce (“x”) .mockReturnValue (真正的);控制台.log(myMock(), myMock(), myMock(), myMock());// 10, 'x', true, true
模拟函数在使用函数延续传递风格的代码中也非常有效。用这种风格编写的代码有助于避免对复杂存根的需要,这些存亚搏取款根重新创建它们所代表的真实组件的行为,有利于在使用它们之前直接将值注入测试。
常量filterTestFn = jest.fn ();//为第一次调用模拟返回' true ',//和' false '为第二个调用filterTestFn.mockReturnValueOnce (真正的) .mockReturnValueOnce (假);常量结果= [11,12] .filter (全国矿工工会= >filterTestFn (num));控制台. log(结果);/ / > [11]控制台. log (filterTestFn.mock.calls [0][0]);/ / 11控制台. log (filterTestFn.mock.calls [0][1]);/ / 12
大多数现实世界的示例实际上都涉及到在依赖组件上获取模拟函数并进行配置,但技术是相同的。在这些情况下,尽量避免在没有直接测试的函数中实现逻辑。
模拟模块
假设我们有一个从API获取用户的类。这个类使用axios调用API,然后返回数据
属性包含所有用户:
/ / users.js进口axios从“axios”;类用户{静态所有(){返回axios.get (' / users.json ') (分别地= >resp.data);}}出口默认的用户;
现在,为了测试这个方法而不实际碰到API(从而创建缓慢而脆弱的测试),我们可以使用jest.mock(…)
函数自动模拟axios模块。
一旦我们模拟了模块,我们就可以提供一个mockResolvedValue
为. get
这会返回我们希望测试断言的数据。实际上,我们是在说我们想要axios.get (' / users.json ')
返回一个假响应。
/ / users.test.js进口axios从“axios”;进口用户从”。/用户的;jest.mock (“axios”);测试(“应该获取用户的, () => {常量用户= [{的名字:“鲍勃”});常量resp = {数据:用户};axios.get.mockResolvedValue(职责);//根据你的用例,你可以使用以下方法:// axios.get.mockImplementation(() => Promise.resolve(resp))返回Users.all () (数据= >期望(数据).toEqual(用户));});
模拟实现
不过,在某些情况下,除了能够指定返回值和完全替换模拟函数的实现之外,它还很有用。这可以用jest.fn
或者是mockImplementationOnce
方法的模拟函数。
常量myMockFn = jest.fn (cb= >cb (零,真正的));myMockFn ((呃,瓦尔) = >控制台. log (val));/ / >真
的mockImplementation
方法在你需要定义从另一个模块创建的模拟函数的默认实现时很有用:
/ / foo.js模块. export =函数(){/ /一些实现;};/ / . jsjest.mock (“. . / foo”);//通过automocking自动实现常量foo =需要(“. . / foo”);// foo是一个mock函数foo.mockImplementation (()= >42);foo ();/ / > 42
当需要重新创建模拟函数的复杂行为,以便多个函数调用产生不同的结果时,请使用mockImplementationOnce
方法:
常量myMockFn = jest .fn() .mockImplementationOnce(cb= >cb (零,真正的) .mockImplementationOnce (cb= >cb (零,假));myMockFn ((呃,瓦尔) = >控制台. log (val));/ / >真myMockFn ((呃,瓦尔) = >控制台. log (val));/ / >假
当被模拟的函数用完定义的实现时mockImplementationOnce
,它将执行默认的实现集jest.fn
(如果有定义):
常量myMockFn = jest .fn(()= >“默认”) .mockImplementationOnce (()= >“第一个电话”) .mockImplementationOnce (()= >“第二个电话”);控制台.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());// > 'first call', 'second call', 'default', 'default'
在这种情况下,我们的方法通常是链接的(因此总是需要返回)这
),我们有一个含糖API,以简化形式为a.mockReturnThis ()
函数也位于所有模拟对象上:
常量myObj = {myMethod: jest.fn () .mockReturnThis ()};//等于常量otherObj = {myMethod: jest.fn (函数(){返回这;}});
假的名字
您可以选择为模拟函数提供一个名称,该名称将在测试错误输出中显示,而不是“jest.fn()”。如果您希望能够快速识别在测试输出中报告错误的模拟函数,请使用此方法。
常量myMockFn = jest .fn() .mockReturnValue()“默认”) .mockImplementation (标量= >42+标量).mockName (“add42”);
定制的匹配器
最后,为了降低断言mock函数是如何被调用的要求,我们为你添加了一些自定义匹配器函数:
// mock函数至少被调用一次期望(mockFunc) .toHaveBeenCalled ();//使用指定的参数至少调用一次模拟函数期望(mockFunc)。toHaveBeenCalledWith (__arg1、最长);//最后一次调用模拟函数是使用指定的参数调用的期望(mockFunc)。toHaveBeenLastCalledWith (__arg1、最长);//所有调用和mock的名称都被写入快照期望(mockFunc) .toMatchSnapshot ();
这些匹配器是常用的检查形式的糖.mock
财产。如果这更符合你的口味,或者你需要做一些更具体的事情,你可以自己手动做:
// mock函数至少被调用一次期望(mockFunc.mock.calls.length) .toBeGreaterThan (0);//使用指定的参数至少调用一次模拟函数期望(mockFunc.mock.calls)。toContainEqual ([__arg1、最长]);//最后一次调用模拟函数是使用指定的参数调用的期望(mockFunc.mock.calls [mockFunc.mock.calls。长度,1])。toEqual([arg1, arg2,]);//最后一次调用mock函数的第一个参数是“42”//(注意,对于这个特定的断言没有糖助手)亚搏取款期望(mockFunc.mock.calls [mockFunc.mock.calls。长度,1][0]) .toBe (42);//一个快照将检查一个mock被调用的次数是否相同,//以相同的顺序,使用相同的参数。它还将对名称进行断言。期望(mockFunc.mock.calls)。toEqual ([[__arg1、最长]]);期望(mockFunc.getMockName ()) .toBe (“一个模拟的名字”);
有关匹配器的完整列表,请查看参考文档yabo2013。