搜索
首页web前端js教程Jest 简介:单元测试、模拟和异步代码

Introduction to Jest: Unit Testing, Mocking, and Asynchronous Code

笑话简介

Jest 是一个用于测试 JavaScript 代码的库。

这是一个由 Facebook 维护的开源项目,它特别适合 React 代码测试,但不仅限于此:它可以测试任何 JavaScript 代码。它的优点是:

  • 速度很快
  • 它可以执行快照测试
  • 它固执己见,提供开箱即用的一切,无需您做出选择
export default function sum(a, n) {
  return a + b;
}

divide.test.js

import sum from './sum';

// Describe the test and wrap it in a function.
it('adds 1 + 2 to equal 3', () => {
  const result = sum(1, 2);

  // Jest uses matchers, like pretty much any other JavaScript testing framework.
  // They're designed to be easy to get at a glance;
  // here, you're expecting `result` to be 3.
  expect(result).toBe(3);
});

匹配器

匹配器是一种可以让您测试值的方法。

  • toBe 比较严格相等,使用 ===
  • toEqual 比较两个变量的值。如果它是一个对象或数组,它会检查所有属性或元素的相等性
  • 传递 null 值时 toBeNull 为 true
  • 传递定义值时 toBeDefined 为 true(与上面相反)
  • 传递未定义值时 toBeUndefine 为 true
  • toBeCloseTo 用于比较浮点值,避免舍入错误
  • toBeTruthy true 如果该值被认为是 true (就像 if 那样)
  • toBeFalsy 如果该值被认为是假(就像 if 一样),则为 true
  • 如果 Expect() 的结果高于参数
  • ,则 toBeGreaterThan true
  • toBeGreaterThanOrEqual 如果 Expect() 的结果等于参数或高于参数
  • ,则为 true
  • 如果 Expect() 的结果低于参数
  • ,则 toBeLessThan true
  • toBeLessThanOrEqual 如果 Expect() 的结果等于参数或低于参数
  • ,则为 true
  • toMatch 用于将字符串与正则表达式模式匹配进行比较
  • toContain 用于数组,如果预期数组在其元素集中包含参数
  • ,则为 true
  • toHaveLength(number):检查数组的长度
  • toHaveProperty(key, value):检查对象是否具有属性,并可选择检查其值
  • toThrow 检查您传递的函数是否抛出异常(一般情况下)或特定异常
  • toBeInstanceOf():检查对象是否是类的实例

依赖关系

依赖项是您的应用程序所依赖的一段代码。它可以是我们项目中的函数/对象或第三方依赖项(例如 axios)

当您自己的应用程序没有一段代码就无法运行时,它就成为真正的依赖项。

例如,如果您在应用程序中实现一项功能来发送电子邮件或发出 api 请求或构建配置对象等

两种方法我们可以在js项目的代码中添加依赖项:

进口

export default function sum(a, n) {
  return a + b;
}

依赖注入

只是一个简单概念的奇特术语。

如果您的函数需要外部依赖项的某些功能,只需将其作为参数注入即可。

import sum from './sum';

// Describe the test and wrap it in a function.
it('adds 1 + 2 to equal 3', () => {
  const result = sum(1, 2);

  // Jest uses matchers, like pretty much any other JavaScript testing framework.
  // They're designed to be easy to get at a glance;
  // here, you're expecting `result` to be 3.
  expect(result).toBe(3);
});

单元测试

单元测试由软件开发人员编写和运行,以确保应用程序的一部分(称为“单元”)满足其设计并按预期运行。

我们想要单独测试我们的代码,我们不关心任何依赖项的实际实现。
我们想要验证

  • 我们的代码单元按预期工作
  • 返回预期结果
  • 按其应有的方式调用任何协作者(依赖项)

这就是模拟我们的依赖关系发挥作用的地方。

嘲笑

在单元测试中,模拟为我们提供了存根依赖项所提供的功能的能力,以及意味着观察我们的代码如何与依赖项交互。

当将依赖项直接包含到我们的测试中成本昂贵或不切实际时,例如,当您的代码对 API 进行 HTTP 调用或与数据库层交互时,模拟特别有用。

最好删除这些依赖项的响应,同时确保它们按要求被调用。这就是模拟派上用场的地方。

通过使用模拟函数,我们可以知道以下内容:

  • 收到的来电数量
  • 参数 每次调用时使用的值。
  • 每次调用时的“上下文”或这个值。
  • 函数如何退出以及产生了哪些值

开玩笑地嘲笑

创建模拟函数有多种方法。

  • jest.fn 方法允许我们直接创建一个新的模拟函数。
  • 如果您正在模拟对象方法,则可以使用 jest.spyOn。
  • 如果你想模拟整个模块,你可以使用 jest.mock。

jest.fn 方法本身就是一个高阶函数。

这是一个工厂方法,用于创建新的、未使用的模拟函数。

JavaScript 中的函数是一等公民,它们可以作为参数传递。

每个模拟函数都有一些特殊的属性。模拟属性是基础。此属性是一个对象,其中包含有关如何调用函数的所有模拟状态信息。该对象包含三个数组属性:

  • 调用 [每次调用的参数]
  • 实例 [每次调用时的“this”值]
  • Results [函数退出的值],results 属性有类型(return 或 throw)和值
    • 函数显式返回一个值。
    • 函数运行完成,没有 return 语句(相当于返回 undefined
    • 函数抛出错误。
export default function sum(a, n) {
  return a + b;
}
  • https://codesandbox.io/s/implementing-mock-functions-tkc8b

模拟基础

import sum from './sum';

// Describe the test and wrap it in a function.
it('adds 1 + 2 to equal 3', () => {
  const result = sum(1, 2);

  // Jest uses matchers, like pretty much any other JavaScript testing framework.
  // They're designed to be easy to get at a glance;
  // here, you're expecting `result` to be 3.
  expect(result).toBe(3);
});

模拟注入的依赖项

import { name, draw, reportArea, reportPerimeter } from './modules/square.js';

模拟模块

使用 jest.fn 模拟函数

// Constructor Injection

// DatabaseManager class takes a database connector as a dependency
class DatabaseManager {
    constructor(databaseConnector) {
        // Dependency injection of the database connector
        this.databaseConnector = databaseConnector;
    }

    updateRow(rowId, data) {
        // Use the injected database connector to perform the update
        this.databaseConnector.update(rowId, data);
    }
}

// parameter injection, takes a database connector instance in as an argument; easy to test!
function updateRow(rowId, data, databaseConnector) {
    databaseConnector.update(rowId, data);
}

这种类型的嘲笑不太常见,原因如下:

  • jest.mock 自动为模块中的所有函数执行此操作
  • jest.spyOn 做同样的事情,但允许恢复原始功能

使用 jest.mock 模拟模块

更常见的方法是使用 jest.mock 自动将模块的所有导出设置为 Mock 函数

// 1. The mock function factory
function fn(impl = () => {}) {
  // 2. The mock function
  const mockFn = function(...args) {
    // 4. Store the arguments used
    mockFn.mock.calls.push(args);
    mockFn.mock.instances.push(this);
    try {
      const value = impl.apply(this, args); // call impl, passing the right this
      mockFn.mock.results.push({ type: 'return', value });
      return value; // return the value
    } catch (value) {
      mockFn.mock.results.push({ type: 'throw', value });
      throw value; // re-throw the error
    }
  }
  // 3. Mock state
  mockFn.mock = { calls: [], instances: [], results: [] };
  return mockFn;
}

使用 jest.spyOn 监视或模拟函数

有时您只想观看一个方法被调用,但保留原始实现。其他时候,您可能想要模拟实现,但稍后在套件中恢复原始版本。

test("returns undefined by default", () => {
  const mock = jest.fn();

  let result = mock("foo");

  expect(result).toBeUndefined();
  expect(mock).toHaveBeenCalled();
  expect(mock).toHaveBeenCalledTimes(1);
  expect(mock).toHaveBeenCalledWith("foo");
});

恢复原来的实现

const doAdd = (a, b, callback) => {
  callback(a + b);
};

test("calls callback with arguments added", () => {
  const mockCallback = jest.fn();
  doAdd(1, 2, mockCallback);
  expect(mockCallback).toHaveBeenCalledWith(3);
});

JavaScript 和事件循环

JavaScript 是单线程的: 一次只能运行一个任务。通常这没什么大不了的,但现在想象一下你正在运行一个需要 30 秒的任务.. 是的.. 在该任务期间,我们等待 30 秒,然后才会发生其他事情(JavaScript 默认在浏览器的主线程上运行,所以整个UI都卡住了)。
现在是 2020 年,没有人想要一个缓慢、反应迟钝的网站。

幸运的是,浏览器为我们提供了一些 JavaScript 引擎本身不提供的功能:Web API。这包括 DOM APIsetTimeoutHTTP 请求 等。这可以帮助我们创建一些异步非阻塞行为

export default function sum(a, n) {
  return a + b;
}
  • 调用堆栈 - 当我们调用一个函数时,它会被添加到称为调用堆栈的东西中。
  • WebAPI - setTimeout 由 WebAPI 提供,采用回调函数并设置计时器,而不阻塞主线程
  • 队列 - 当计时器结束时,回调被添加到队列
  • 事件循环 - 检查调用堆栈是否为空,检查队列中是否有要执行的回调,然后移至要执行的调用堆栈
import sum from './sum';

// Describe the test and wrap it in a function.
it('adds 1 + 2 to equal 3', () => {
  const result = sum(1, 2);

  // Jest uses matchers, like pretty much any other JavaScript testing framework.
  // They're designed to be easy to get at a glance;
  // here, you're expecting `result` to be 3.
  expect(result).toBe(3);
});

使用 Jest 测试异步代码

Jest 通常希望同步执行测试的函数

如果我们执行异步操作,但我们不让 Jest 知道它应该等待测试结束,则会给出误报。

import { name, draw, reportArea, reportPerimeter } from './modules/square.js';

异步模式
JavaScript 中有多种处理异步操作的模式;最常用的是:

  • 回调
  • Promise 和异步/等待

测试回调

你不能在回调中进行测试,因为 Jest 不会执行它 - 测试文件的执行在回调被调用之前结束。要解决此问题,请将参数传递给测试函数,您可以方便地调用“done”。 Jest 将等到您调用 done() 后再结束测试:

// Constructor Injection

// DatabaseManager class takes a database connector as a dependency
class DatabaseManager {
    constructor(databaseConnector) {
        // Dependency injection of the database connector
        this.databaseConnector = databaseConnector;
    }

    updateRow(rowId, data) {
        // Use the injected database connector to perform the update
        this.databaseConnector.update(rowId, data);
    }
}

// parameter injection, takes a database connector instance in as an argument; easy to test!
function updateRow(rowId, data, databaseConnector) {
    databaseConnector.update(rowId, data);
}

承诺

对于返回 Promise 的函数,我们从测试中返回一个 Promise:

// 1. The mock function factory
function fn(impl = () => {}) {
  // 2. The mock function
  const mockFn = function(...args) {
    // 4. Store the arguments used
    mockFn.mock.calls.push(args);
    mockFn.mock.instances.push(this);
    try {
      const value = impl.apply(this, args); // call impl, passing the right this
      mockFn.mock.results.push({ type: 'return', value });
      return value; // return the value
    } catch (value) {
      mockFn.mock.results.push({ type: 'throw', value });
      throw value; // re-throw the error
    }
  }
  // 3. Mock state
  mockFn.mock = { calls: [], instances: [], results: [] };
  return mockFn;
}

异步/等待

为了测试返回 Promise 的函数,我们还可以使用 async/await,这使得语法非常简单明了:

test("returns undefined by default", () => {
  const mock = jest.fn();

  let result = mock("foo");

  expect(result).toBeUndefined();
  expect(mock).toHaveBeenCalled();
  expect(mock).toHaveBeenCalledTimes(1);
  expect(mock).toHaveBeenCalledWith("foo");
});

尖端

  • 我们需要很好地理解我们的函数的作用以及我们要测试的内容
  • 思考我们正在测试的代码的行为
  • 设置舞台:
    • 模拟/监视任何依赖项
    • 我们的代码是否与全局对象交互?我们也可以嘲笑/监视他们
    • 我们的测试是否与 DOM 交互?我们可以构建一些假元素来使用
    • 构建你的测试
    • 鉴于...
    • 当我打电话时......
    • 然后...我期待......
const doAdd = (a, b, callback) => {
  callback(a + b);
};

test("calls callback with arguments added", () => {
  const mockCallback = jest.fn();
  doAdd(1, 2, mockCallback);
  expect(mockCallback).toHaveBeenCalledWith(3);
});

链接

  • https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c
  • https://jestjs.io/docs/en/mock-functions
  • https://codesandbox.io/s/implementing-mock-functions-tkc8b
  • https://github.com/BulbEnergy/jest-mock-examples
  • https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
  • https://jestjs.io/docs/en/asynchronous
  • https://www.pluralsight.com/guides/test-asynchronous-code-jest

以上是Jest 简介:单元测试、模拟和异步代码的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Python和JavaScript的未来:趋势和预测Python和JavaScript的未来:趋势和预测Apr 27, 2025 am 12:21 AM

Python和JavaScript的未来趋势包括:1.Python将巩固在科学计算和AI领域的地位,2.JavaScript将推动Web技术发展,3.跨平台开发将成为热门,4.性能优化将是重点。两者都将继续在各自领域扩展应用场景,并在性能上有更多突破。

Python vs. JavaScript:开发环境和工具Python vs. JavaScript:开发环境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

JavaScript是用C编写的吗?检查证据JavaScript是用C编写的吗?检查证据Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C语言编写的。1)C语言提供了高效性能和底层控制,适合JavaScript引擎的开发。2)以V8引擎为例,其核心用C 编写,结合了C的效率和面向对象特性。3)JavaScript引擎的工作原理包括解析、编译和执行,C语言在这些过程中发挥关键作用。

JavaScript的角色:使网络交互和动态JavaScript的角色:使网络交互和动态Apr 24, 2025 am 12:12 AM

JavaScript是现代网站的核心,因为它增强了网页的交互性和动态性。1)它允许在不刷新页面的情况下改变内容,2)通过DOMAPI操作网页,3)支持复杂的交互效果如动画和拖放,4)优化性能和最佳实践提高用户体验。

C和JavaScript:连接解释C和JavaScript:连接解释Apr 23, 2025 am 12:07 AM

C 和JavaScript通过WebAssembly实现互操作性。1)C 代码编译成WebAssembly模块,引入到JavaScript环境中,增强计算能力。2)在游戏开发中,C 处理物理引擎和图形渲染,JavaScript负责游戏逻辑和用户界面。

从网站到应用程序:JavaScript的不同应用从网站到应用程序:JavaScript的不同应用Apr 22, 2025 am 12:02 AM

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

Python vs. JavaScript:比较用例和应用程序Python vs. JavaScript:比较用例和应用程序Apr 21, 2025 am 12:01 AM

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

C/C在JavaScript口译员和编译器中的作用C/C在JavaScript口译员和编译器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能