写在前面:

  在写单元测试的时候有一个最重要的步骤就是Mock,我们通常会根据接口来Mock接口的实现,比如你要测试某个class中的某个方法,而这个方法又依赖了外部的一些接口的实现,从单元测试的角度来说我只关心我测试的方法的内部逻辑,我并不关注与当前class本身依赖的实现,所以我们通常会Mock掉依赖接口的返回,因为我们的测试重点在于特定的方法,所以在Jest中同样提供了Mock的功能,本节主要介绍Jest的Mock Function的功能。

Jest中的Mock Function

Mock 函数可以轻松地测试代码之间的连接——这通过擦除函数的实际实现,捕获对函数的调用 ( 以及在这些调用中传递的参数) ,在使用 new 实例化时捕获构造函数的实例,或允许测试时配置返回值的形式来实现。Jest中有两种方式的Mock Function,一种是利用Jest提供的Mock Function创建,另外一种是手动创建来覆写本身的依赖实现。

假设我们要测试函数 forEach 的内部实现,这个函数为传入的数组中的每个元素调用一个回调函数,代码如下:

function forEach(items, callback) {
for (let index = 0; index < items.length; index++) {
callback(items[index]);
}
}

为了测试此函数,我们可以使用一个 mock 函数,然后检查 mock 函数的状态来确保回调函数如期调用。

const mockCallback = jest.fn();
forEach([0, 1], mockCallback); // 此模拟函数被调用了两次
expect(mockCallback.mock.calls.length).toBe(2); // 第一次调用函数时的第一个参数是 0
expect(mockCallback.mock.calls[0][0]).toBe(0); // 第二次调用函数时的第一个参数是 1
expect(mockCallback.mock.calls[1][0]).toBe(1);

几乎所有的Mock Function都带有 .mock的属性,它保存了此函数被调用的信息。 .mock 属性还追踪每次调用时 this的值,所以也让检视 this 的值成为可能:

const myMock = jest.fn();

const a = new myMock();
const b = {};
const bound = myMock.bind(b);
bound(); console.log(myMock.mock.instances);

在测试中,需要对函数如何被调用,或者实例化做断言时,这些 mock 成员变量很有帮助意义︰

// 这个函数只调用一次
expect(someMockFunction.mock.calls.length).toBe(1); // 这个函数被第一次调用时的第一个 arg 是 'first arg'
expect(someMockFunction.mock.calls[0][0]).toBe('first arg'); // 这个函数被第一次调用时的第二个 arg 是 'second arg'
expect(someMockFunction.mock.calls[0][1]).toBe('second arg'); // 这个函数被实例化两次
expect(someMockFunction.mock.instances.length).toBe(2); // 这个函数被第一次实例化返回的对象中,有一个 name 属性,且被设置为了 'test’
expect(someMockFunction.mock.instances[0].name).toEqual('test');

Mock 函数也可以用于在测试期间将测试值注入您的代码︰

const myMock = jest.fn();
console.log(myMock());
// > undefined myMock
.mockReturnValueOnce(10)
.mockReturnValueOnce('x')
.mockReturnValue(true); console.log(myMock(), myMock(), myMock(), myMock());

用于函数连续传递风格(CPS)的代码中时,Mock 函数也非常有效。 以这种风格编写的代码有助于避免那种需要通过复杂的中间值,来重建他们在真实组件的行为,这有利于在它们被调用之前将值直接注入到测试中。

const filterTestFn = jest.fn();

// Make the mock return `true` for the first call,
// and `false` for the second call
filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false); const result = [11, 12].filter(filterTestFn); console.log(result);
// > [11]
console.log(filterTestFn.mock.calls);
// > [ [11], [12] ]

大多数现实世界的例子实际上都涉及到将一个被依赖的组件上使用 mock 函数替代并进行配置,这在技术上(和上面的描述)是相同的。 在这些情况下,尽量避免在非真正想要进行测试的任何函数内实现逻辑。

有些情况下超越指定返回值的功能是有用的,并且全面替换了模拟函数的实现。

const myMockFn = jest.fn(cb => cb(null, true));

myMockFn((err, val) => console.log(val));
// > true myMockFn((err, val) => console.log(val));
// > true

如果你需要定义一个模拟的函数,它从另一个模块中创建的默认实现,mockImplementation方法非常有用︰

// foo.js
module.exports = function() {
// some implementation;
}; // test.js
jest.mock('../foo'); // this happens automatically with automocking
const foo = require('../foo'); // foo is a mock function
foo.mockImplementation(() => 42);
foo();
// > 42

当你需要重新创建复杂行为的模拟功能,这样多个函数调用产生不同的结果时,请使用 mockImplementationOnce 方法︰

const myMockFn = jest
.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false)); myMockFn((err, val) => console.log(val));
// > true myMockFn((err, val) => console.log(val));
// > false

当指定的mockImplementationOnce 执行完成之后将会执行默认的被jest.fn定义的默认实现,前提是它已经被定义过。

const myMockFn = jest
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call'); console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
// > 'first call', 'second call', 'default', 'default'

对于有通常链接的方法(因此总是需要返回this)的情况,我们有一个语法糖的API以.mockReturnThis()函数的形式来简化它,它也位于所有模拟器上:

const myObj = {
myMethod: jest.fn().mockReturnThis(),
}; // is the same as const otherObj = {
myMethod: jest.fn(function() {
return this;
}),
};

你也可以给你的Mock Function起一个准确的名字,这样有助于你在测试错误的时候在输出窗口定位到具体的Function

const myMockFn = jest
.fn()
.mockReturnValue('default')
.mockImplementation(scalar => 42 + scalar)
.mockName('add42');

最后,为了更简单地说明如何调用mock函数,我们为您添加了一些自定义匹配器函数:

// The mock function was called at least once
expect(mockFunc).toBeCalled(); // The mock function was called at least once with the specified args
expect(mockFunc).toBeCalledWith(arg1, arg2); // The last call to the mock function was called with the specified args
expect(mockFunc).lastCalledWith(arg1, arg2); // All calls and the name of the mock is written as a snapshot
expect(mockFunc).toMatchSnapshot();

这些匹配器是真的只是语法糖的常见形式的检查 .mock 属性。 你总可以手动自己如果是更合你的口味,或如果你需要做一些更具体的事情︰

// The mock function was called at least once
expect(mockFunc.mock.calls.length).toBeGreaterThan(0); // The mock function was called at least once with the specified args
expect(mockFunc.mock.calls).toContain([arg1, arg2]); // The last call to the mock function was called with the specified args
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([
arg1,
arg2,
]); // The first arg of the last call to the mock function was `42`
// (note that there is no sugar helper for this specific of an assertion)
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(42); // A snapshot will check that a mock was invoked the same number of times,
// in the same order, with the same arguments. It will also assert on the name.
expect(mockFunc.mock.calls).toEqual([[arg1, arg2]]);
expect(mockFunc.mock.getMockName()).toBe('a mock name');

写在最后:

本文只是简单的介绍了Mock Function的功能,更完整的匹配器列表,请查阅 参考文档

系列教程:

   1. 前端测试框架Jest系列教程 -- Matchers(匹配器)

   2.前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)

   3.前端测试框架Jest系列教程 -- Mock Functions(模拟器)

   4.前端测试框架Jest系列教程 -- Global Functions(全局函数)

前端测试框架Jest系列教程 -- Mock Functions的更多相关文章

  1. 前端测试框架Jest系列教程 -- Mock Functions(模拟器)

    写在前面: 在写单元测试的时候有一个最重要的步骤就是Mock,我们通常会根据接口来Mock接口的实现,比如你要测试某个class中的某个方法,而这个方法又依赖了外部的一些接口的实现,从单元测试的角度来 ...

  2. 前端测试框架Jest系列教程 -- Global Functions(全局函数)

    写在前面: Jest中定义了很多全局性的Function供我们使用,我们不必再去引用别的包来去实现类似的功能,下面将列举Jest中实现的全局函数. Jest Global Functions afte ...

  3. 前端测试框架Jest系列教程 -- 简介

    写在前面: 随着互联网日新月异的发展,用户对于页面的美观度,流畅度以及各方面的体验有了更高的要求,我们的网页不再是简单的承载文字,图片等简单的信息传递给用户,我们需要的是更加美观的页面展示,更快的浏览 ...

  4. 前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)

    写在前面: 在JavaScript代码中,异步运行是很常见的.当你有异步运行的代码时,Jest需要知道它测试的代码何时完成,然后才能继续进行另一个测试.Jest提供了几种方法来处理这个问题. 测试异步 ...

  5. 前端测试框架Jest系列教程 -- Matchers(匹配器)

    写在前面: 匹配器(Matchers)是Jest中非常重要的一个概念,它可以提供很多种方式来让你去验证你所测试的返回值,本文重点介绍几种常用的Matcher,其他的可以通过官网api文档查看. 常用的 ...

  6. 前端测试框架Jest系列教程 -- Expect(验证)

    写在前面 在编写测试时,我们通常需要检查值是否满足某些条件,Jest中提供的expect允许你访问很多“Matchers”,这些“匹配器”允许您验证不同的东西. Expect 可以验证什么 Jest中 ...

  7. 前端测试框架jest 简介

    转自: https://www.cnblogs.com/Wolfmanlq/p/8012847.html 作者:Ken Wang 出处:http://www.cnblogs.com/Wolfmanlq ...

  8. 前端测试框架 Jest

    前端测试工具一览 前端测试工具也和前端的框架一样纷繁复杂,其中常见的测试工具,大致可分为测试框架.断言库.测试覆盖率工具等几类.在正式开始本文之前,我们先来大致了解下它们: 测试框架 测试框架的作用是 ...

  9. 前端测试框架Jest——语法篇

    使用匹配器 使用不同匹配器可以测试输入输出的值是否符合预期.下面介绍一些常见的匹配器.普通匹配器最简单的测试值的方法就是看是否精确匹配.首先是toBe() test('two plus two is ...

随机推荐

  1. java springmvc+bui+bootstrap后台管理系统搭建

    先来说说bui,这个框架是阿里巴巴的一个前端团队研发的,能够用很少的代码快速搭建一个后台管理系统,很适做管理平台的开发, 之前用过类似这样的框架extjs,做个比较,这个框架实现功能比extjs的代码 ...

  2. RxSwift 对 MJRefresh 使用的封装

    对于一个很常用的两个库, MJRefresh 如何可以像 UIButton 使用方式呢: btn.rx.tap.subscribe(...) Rxswift 中的很多类似处理的方式都使用了跟下面极为相 ...

  3. Struts2+Spring+Hibernate实现员工管理增删改查功能(一)之ssh框架整合

    前言        转载请标明出处:http://www.cnblogs.com/smfx1314/p/7795837.html 本项目是我写的一个练习,目的是回顾ssh框架的整合以及使用.项目介绍: ...

  4. 一起写框架-控制反转(Ioc)概述(二)

    控制反转概述 控制反转(Inversion of Control,英文缩写为IoC),就是将代码的调用的控制权,由调用方转移给被调用方. 如图:修改代码A类的代码,才能将B类的对象换成C类.代码的控制 ...

  5. Varnish 实战项目

    实现基于Keepalived+Haproxy+Varnish+LNMP企业级架构 原理:缓存,又称加速器,用于加速运行速度较快的设备与较慢设备之间的通信.基于程序的运行具有局部性特征其能实现加速的功能 ...

  6. Centos下配置tomcat7的https证书

    近期搞定了HTTPS配置,特此记录. 1.把下载的文件拷贝到cert文件夹,然后放在tomcat根目录下(与conf同一级目录).2.配置conf下的server.xml,修改下面3个节点,如下: & ...

  7. 深入探讨List<>中的一个姿势。

    List<>是c#中很常见的一种集合形式,近期在阅读c#源码时,发现了一个很有意思的定义: [DebuggerTypeProxy(typeof(Mscorlib_CollectionDeb ...

  8. HTTPS 建立连接的详细过程

    HTTPS是在HTTP的基础上和ssl/tls证书结合起来的一种协议,保证了传输过程中的安全性,减少了被恶意劫持的可能.很好的解决了解决了http的三个缺点(被监听.被篡改.被伪装) 对称加密和非对称 ...

  9. 掌握numpy(一)

    NumPy是一款用于科学计算的python包,强大之处在于矩阵的运算以及包含丰富的线性代数运算的支持.本文将对numpy一些常用的用法进行讲解,一来是对自己的知识进行梳理,二来作为一份备忘录供以后查阅 ...

  10. Knowledge_SPA——精研查找算法

    首先保证这一篇分析查找算法的文章,气质与大部分搜索引擎搜索到的文章不同,主要体现在代码上面,会更加高级,会结合到很多之前研究过的内容,例如设计模式,泛型等.这也与我的上一篇面向程序员编程--精研排序算 ...