资讯专栏INFORMATION COLUMN

使用Jest测试JavaScript(Mock篇)

YorkChen / 1082人阅读

摘要:测试调用断言的执行后返回断言被调用断言被调用了一次断言传入的参数为所创建的函数还可以设置返回值,定义内部实现或返回对象。

在本篇教程中,我们会介绍 Jest 中的三个与 Mock 函数相关的API,分别是jest.fn()jest.spyOn()jest.mock()。使用它们创建Mock函数能够帮助我们更好的测试项目中一些逻辑较复杂的代码,例如测试函数的嵌套调用,回调函数的调用等。

如果你还不知道Jest的基本使用方法,请先阅读: 《使用Jest测试JavaScript (入门篇)》
为什么要使用Mock函数?

在项目中,一个模块的方法内常常会去调用另外一个模块的方法。在单元测试中,我们可能并不需要关心内部调用的方法的执行过程和结果,只想知道它是否被正确调用即可,甚至会指定该函数的返回值。此时,使用Mock函数是十分有必要。

Mock函数提供的以下三种特性,在我们写测试代码时十分有用:

捕获函数调用情况

设置函数返回值

改变函数的内部实现

我们接着使用上篇文章中的目录结构,在test/functions.test.js文件中编写测试代码,src/目录下写被测试代码。
1. jest.fn()

jest.fn()是创建Mock函数最简单的方式,如果没有定义函数内部的实现,jest.fn()会返回undefined作为返回值。

// functions.test.js

test("测试jest.fn()调用", () => {
  let mockFn = jest.fn();
  let result = mockFn(1, 2, 3);

  // 断言mockFn的执行后返回undefined
  expect(result).toBeUndefined();
  // 断言mockFn被调用
  expect(mockFn).toBeCalled();
  // 断言mockFn被调用了一次
  expect(mockFn).toBeCalledTimes(1);
  // 断言mockFn传入的参数为1, 2, 3
  expect(mockFn).toHaveBeenCalledWith(1, 2, 3);
})

jest.fn()所创建的Mock函数还可以设置返回值定义内部实现返回Promise对象

// functions.test.js

test("测试jest.fn()返回固定值", () => {
  let mockFn = jest.fn().mockReturnValue("default");
  // 断言mockFn执行后返回值为default
  expect(mockFn()).toBe("default");
})

test("测试jest.fn()内部实现", () => {
  let mockFn = jest.fn((num1, num2) => {
    return num1 * num2;
  })
  // 断言mockFn执行后返回100
  expect(mockFn(10, 10)).toBe(100);
})

test("测试jest.fn()返回Promise", async () => {
  let mockFn = jest.fn().mockResolvedValue("default");
  let result = await mockFn();
  // 断言mockFn通过await关键字执行后返回值为default
  expect(result).toBe("default");
  // 断言mockFn调用后返回的是Promise对象
  expect(Object.prototype.toString.call(mockFn())).toBe("[object Promise]");
})

上面的代码是jest.fn()提供的几个常用的API和断言语句,下面我们在src/fetch.js文件中写一些被测试代码,以更加接近业务的方式来理解Mock函数的实际应用。

被测试代码中依赖了axios这个常用的请求库和JSONPlaceholder这个上篇文章中提到免费的请求接口,请先在shell中执行npm install axios --save安装依赖,。
// fetch.js

import axios from "axios";

export default {
  async fetchPostsList(callback) {
    return axios.get("https://jsonplaceholder.typicode.com/posts").then(res => {
      return callback(res.data);
    })
  }
}

我们在fetch.js中封装了一个fetchPostsList方法,该方法请求了JSONPlaceholder提供的接口,并通过传入的回调函数返回处理过的返回值。如果我们想测试该接口能够被正常请求,只需要捕获到传入的回调函数能够被正常的调用即可。下面是functions.test.js中的测试的代码。

import fetch from "../src/fetch.js"

test("fetchPostsList中的回调函数应该能够被调用", async () => {
  expect.assertions(1);
  let mockFn = jest.fn();
  await fetch.fetchPostsList(mockFn);

  // 断言mockFn被调用
  expect(mockFn).toBeCalled();
})
2. jest.mock()

fetch.js文件夹中封装的请求方法可能我们在其他模块被调用的时候,并不需要进行实际的请求(请求方法已经通过单侧或需要该方法返回非真实数据)。此时,使用jest.mock()去mock整个模块是十分有必要的。

下面我们在src/fetch.js的同级目录下创建一个src/events.js

// events.js

import fetch from "./fetch";

export default {
  async getPostList() {
    return fetch.fetchPostsList(data => {
      console.log("fetchPostsList be called!");
      // do something
    });
  }
}

functions.test.js中的测试代码如下:

// functions.test.js

import events from "../src/events";
import fetch from "../src/fetch";

jest.mock("../src/fetch.js");

test("mock 整个 fetch.js模块", async () => {
  expect.assertions(2);
  await events.getPostList();
  expect(fetch.fetchPostsList).toHaveBeenCalled();
  expect(fetch.fetchPostsList).toHaveBeenCalledTimes(1);
});

在测试代码中我们使用了jest.mock("../src/fetch.js")去mock整个fetch.js模块。如果注释掉这行代码,执行测试脚本时会出现以下报错信息

从这个报错中,我们可以总结出一个重要的结论:

在jest中如果想捕获函数的调用情况,则该函数必须被mock或者spy!
3. jest.spyOn()

jest.spyOn()方法同样创建一个mock函数,但是该mock函数不仅能够捕获函数的调用情况,还可以正常的执行被spy的函数。实际上,jest.spyOn()jest.fn()的语法糖,它创建了一个和被spy的函数具有相同内部代码的mock函数。

上图是之前jest.mock()的示例代码中的正确执行结果的截图,从shell脚本中可以看到console.log("fetchPostsList be called!");这行代码并没有在shell中被打印,这是因为通过jest.mock()后,模块内的方法是不会被jest所实际执行的。这时我们就需要使用jest.spyOn()

// functions.test.js

import events from "../src/events";
import fetch from "../src/fetch";

test("使用jest.spyOn()监控fetch.fetchPostsList被正常调用", async() => {
  expect.assertions(2);
  const spyFn = jest.spyOn(fetch, "fetchPostsList");
  await events.getPostList();
  expect(spyFn).toHaveBeenCalled();
  expect(spyFn).toHaveBeenCalledTimes(1);
})

执行npm run test后,可以看到shell中的打印信息,说明通过jest.spyOn()fetchPostsList被正常的执行了。

4. 总结

这篇文章中我们介绍了jest.fn(),jest.mock()jest.spyOn()来创建mock函数,通过mock函数我们可以通过以下三个特性去更好的编写我们的测试代码:

捕获函数调用情况

设置函数返回值

改变函数的内部实现

在实际项目的单元测试中,jest.fn()常被用来进行某些有回调函数的测试;jest.mock()可以mock整个模块中的方法,当某个模块已经被单元测试100%覆盖时,使用jest.mock()去mock该模块,节约测试时间和测试的冗余度是十分必要;当需要测试某些必须被完整执行的方法时,常常需要使用jest.spyOn()。这些都需要开发者根据实际的业务代码灵活选择。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98452.html

相关文章

  • [gist]用 jest 轻松测试 JavaScript

    摘要:的测试不管在用还是都是很头疼的事情但是自从有了一口气写个测试腰也不疼了头也不疼了只需要个理由在说用测为什么好之前我们先来看我们要测的一个例子栗子比如我要写一个模块要去取用户的和他所有的数量那么我们应该有一个的我们还需要一个的大同小异略去最后 from oyanglul.us Javascript 的测试, 不管在用 jasmine 还是 mocha, 都是很头疼的事情. 但是自从有了...

    CoderStudy 评论0 收藏0
  • 提高代码质量——使用Jest和Sinon给已有的代码添加单元测试

    摘要:现在,我们可以使用单元测试来提高自己的代码质量。它在单元测试的编写中通常用来模拟等相关请求。通过这篇文章,你应该学会了如何针对已有代码从零开始编写一套完整的单元测试用例。 概述 在日常的功能开发中,我们的代码测试都依赖于自己或者QA进行测试。这些操作不仅费时费力,而且还依赖开发者自身的驱动。在开发一些第三方依赖的库时,我们也没有办法给第三方提供完整的代码质量报告。 现在,我们可以使用单...

    voyagelab 评论0 收藏0
  • React 测试指南

    摘要:单元测试针对程序模块进行测试。是开源的单元测试工具。一个好的单元测试应该具备的条件安全重构已有代码单元测试一个很重要的价值是为重构保驾护航。断言外部依赖单元测试的一个重要原则就是无依赖和隔离。 前端测试金字塔 对于一个 Web 应用来说,理想的测试组合应该包含大量单元测试(unit tests),部分快照测试(snapshot tests),以及少量端到端测试(e2e tests)。参...

    Tecode 评论0 收藏0
  • Jest + Enzyme 前端自动化测试

    摘要:简介是发布的一个开源的基于框架的单元测试工具。具体版本对照如下版本版本此处使用的版本为,所以我们需要安装依赖安装完成,接下来需要进行相关的配置。这样就可以将测试集中在组件的结构和逻辑上。 Jest、Enzyme 简介 Jest 是 Facebook 发布的一个开源的、基于 Jasmine 框架的 JavaScript 单元测试工具。 Enzyme 是 React 的测试类库。 Enzy...

    xushaojieaaa 评论0 收藏0
  • 【全栈React】第23天: 实现测试

    摘要:包包含由团队提供的测试实用程序。将在一个名为的目录中自动查找整个树中的测试文件是的带有下划线。让我们为时间轴组件创建第一个测试。其中之一是命令。现在我们已经编写了第一个测试并确认了我们的设置我们将在明天开始测试我们的时间轴组件。 本文转载自:众成翻译译者:iOSDevLog链接:http://www.zcfy.cc/article/3807原文:https://www.fullstac...

    airborne007 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<