测试框架 Jest 实用教程

news2024/11/28 22:01:26

官网 https://jestjs.io/docs/getting-started

安装

cnpm i --save-dev jest

使用

  1. 在项目中的任意位置(通常单独建个名为 test 的文件夹)新建以 .test.js 为后缀的测试文件,如 expect.test.js (若项目中有 test.js 存在,请改为其他名称,JEST 会将 test.js 也视为测试文件)

  2. 在 package.json 中添加测试脚本

  "scripts": {
    "test": "jest",

则执行 npm run test 时,便会使用 jest 执行所有的 .test.js 为后缀的测试文件

  1. expect.test.js 中添加内容
test("断言2+2=4", () => {
  expect(2 + 2).toBe(4);
});

以上即一个测试案例,用于断言 2+2=4
此处的 test 也可以用 it

  1. 执行 npm run test
    vscode中可以直接点击脚本执行按钮
    在这里插入图片描述
    效果如下:
    在这里插入图片描述
    全部绿色,即测试通过。

若想测试一直执行,可以使用

npx jest 测试文件名称 --watch

断言

相等 toBe

expect(2 + 2).toBe(4);

执行的是 ES6 的Object.is,与严格相等运算符(===)基本一致,不会进行强制类型转换,不同之处为 +0不等于-0,NaN等于自身,对引用类型的数据(如对象、数组等),比较其地址是否相同。

相等 toEqual

会比较数组/对象的每一项,但会忽略 undefined

// 测试通过
test("toEqual断言对象相等", () => {
  expect({ a: 1 }).toEqual({ a: 1, b: undefined });
});

严格相等 toStrictEqual

与 toEqual 类似,但不会忽略 undefined

// 测试报错
test("toStrictEqual断言对象相等", () => {
  expect({ a: 1 }).toStrictEqual({ a: 1, b: undefined });
});

不相等 not

在判断相等前添加 not 即可

test("断言2+3不等于4", () => {
  expect(2 + 3).not.toBe(4);
});

特殊值的断言

  • toBeNull() 断言为 null
  • toBeUndefined() 断言为 undefined
  • toBeTruthy() 断言为 true
  • toBeFalsy() 断言为 false

比较

  • toBeGreaterThan(3) 大于3
  • toBeGreaterThanOrEqual(3.5) 大于等于3.5
  • toBeLessThan(5) 小于5
  • toBeLessThanOrEqual(4.5) 小于等于4.5

浮点数的计算结果比较 toBeCloseTo

因 js 无法精确计算浮点数,不能用 toBe ,而要用 toBeCloseTo

test('断言 0.1+0.2=0.3', () => {
  const value = 0.1 + 0.2;
  //expect(value).toBe(0.3); 此方式会断言失败   
  expect(value).toBeCloseTo(0.3); 
});

字符串包含 toMatch

// Christoph 中包含 stop
expect('Christoph').toMatch(/stop/);

数组中查找指定项 toContain

expect(shoppingList).toContain('milk');

更多断言用法见 https://jestjs.io/docs/expect#expectvalue

异步测试

回调函数

const callbackFunc = (cb) => {
  setTimeout(() => {
    cb("hello");
  }, 100);
};

test("callback", (done) => {
  callbackFunc((data) => {
    //等待异步执行
    done();
    expect(data).toBe("hello");
  });
});

Promise

const promiseFuc = () => Promise.resolve("hello");

test("promise", () => {
  return promiseFuc().then((data) => {
    expect(data).toBe("hello");
  });
});

await 写法

const promiseFuc = () => Promise.resolve("hello");

test("await async", async () => {
  const data = await promiseFuc();
  expect(data).toBe("hello");
});

属性 resolves 写法

const promiseFuc = () => Promise.resolve("hello");

test("resolves", () => {
  return expect(promiseFuc()).resolves.toBe("hello");
});

属性 rejects 写法

const promiseFuc = () => Promise.reject("error");

test("rejects", () => {
  return expect(promiseFuc()).rejects.toBe("error");
});

模拟 mock

模拟数据

function mockTestFunc(cb) {
  return cb(3);
}

test("mock", () => {
  // jest.fn() 是对 mock 过程的监听
  const mockCB = jest.fn();
  // 调用mock函数
  mockTestFunc(mockCB);
  // 测试mock函数是否执行
  expect(mockCB).toHaveBeenCalled();
  // 测试mock函数回调的参数是否为3
  expect(mockCB).toHaveBeenCalledWith(3);
  // 测试mock函数回调的次数是否为1
  expect(mockCB).toHaveBeenCalledTimes(1);
  // 打印mock对象,可查看相关属性
  console.log(mockCB.mock);
});

打印内容为:

    {
      calls: [ [ 3 ] ],
      contexts: [ undefined ],
      instances: [ undefined ],
      invocationCallOrder: [ 1 ],
      results: [ { type: 'return', value: undefined } ],
      lastCall: [ 3 ]
    }

上例中没有对回调的参数进行二次处理,所以 value 为 undefined
jest.fn() 中可以对回调的参数进行二次处理,得到对应的值 value

function mockTestFunc(cb) {
  return cb(3);
}

test("mock二次处理", () => {
  // jest.fn() 是对 mock 过程的监听
  const mockCB = jest.fn((x) => x * 2);
  // 调用mock函数
  mockTestFunc(mockCB);
  // 打印mock对象,可查看相关属性
  console.log(mockCB.mock);
});

打印结果为:

    {
      calls: [ [ 3 ] ],
      contexts: [ undefined ],
      instances: [ undefined ],
      invocationCallOrder: [ 1 ],
      results: [ { type: 'return', value: 6 } ],
      lastCall: [ 3 ]
    }

得到值 value 为 6

模拟第三方库

真实请求如下:
user.js

const axios = require("axios");

module.exports = function getUserName(id) {
  return axios
    .get(`https://jsonplaceholder.typicode.com/users/${id}`)
    .then((res) => {
      return res.data.username;
    });
};

mock.test.js

const getUserName = require("./user.js");

test("真实请求第三方库-axios", () => {
  return getUserName(1).then((name) => {
    console.log(name);
  });
});

mock 写法1

const getUserName = require("./user.js");
const axios = require("axios");
jest.mock("axios");

axios.get.mockImplementation(() => {
  return Promise.resolve({
    data: {
      username: "朝阳",
    },
  });
});

test("mock 第三方库-axios", () => {
  return getUserName(1).then((name) => {
    console.log(name);
  });
});

此时的 mockCB 即 axios.get ,可以进行监听

test("mock 第三方库-axios", () => {
  return getUserName(1).then((name) => {
    // 监听 axios.get 是否被调用
    expect(axios.get).toHaveBeenCalled();
  });
});

mock 写法2

使用 mockResolvedValue 直接模拟 Promise 的返回值。

axios.get.mockResolvedValue({
  data: {
    username: "朝阳",
  },
});

mock 写法3 【推荐】

项目目录下新建文件夹 __mocks__,在__mocks__文件夹中按模拟的第三方库名称新建 js 文件,如 axios.js,内容为

const axios = {
  get: jest.fn(() => Promise.resolve({ data: { username: "朝阳" } })),
};

module.exports = axios;

如此,则测试文件中的 axios 请求,都会按 axios.js 中逻辑执行。

const getUserName = require("./user.js");

test("mock 第三方库-axios", () => {
  return getUserName(1).then((name) => {
    console.log(name);
  });
});

会打印 朝阳

时间控制

执行所有定时器

const callbackFunc = (cb) => {
  setTimeout(() => {
    cb("hello");
  }, 1000);
};

test("回调是否执行", () => {
  const callback = jest.fn();
  callbackFunc(callback);
  // 测试 callback 是否执行
  expect(callback).toHaveBeenCalled();
});

此处因 setTimeout 导航回调函数 1秒后 执行,所以测试会报错
解决方案是通过 jest 接管时间控制,修正后代码如下(详见备注):

//  jest 接管时间控制
jest.useFakeTimers();

test("回调是否执行", () => {
  const callback = jest.fn();
  callbackFunc(callback);
  // 执行所有定时器
  jest.runAllTimers();
  expect(callback).toHaveBeenCalled();
});

分步执行定时器

// setTimeout的嵌套:1秒后调用 one ,再过2秒后调用 two
const callbackFunc = (cb) => {
  setTimeout(() => {
    cb("one");
    setTimeout(() => {
      cb("two");
    }, 2000);
  }, 1000);
};

//  jest 接管时间控制
jest.useFakeTimers();

test("回调是否执行", () => {
  const callback = jest.fn();
  callbackFunc(callback);
  // 执行一个定时器
  jest.runOnlyPendingTimers();
  expect(callback).toHaveBeenLastCalledWith("one");
  // 又执行一个定时器
  jest.runOnlyPendingTimers();
  expect(callback).toHaveBeenLastCalledWith("two");
});

指定代码运行的时间

jest.advanceTimersByTime(代码执行时间),单位为毫秒

const callbackFunc = (cb) => {
  setTimeout(() => {
    cb("one");
  }, 1000);
};

//  jest 接管时间控制
jest.useFakeTimers();

test("回调是否执行", () => {
  const callback = jest.fn();
  callbackFunc(callback);
  // 500毫秒后
  jest.advanceTimersByTime(500);
  // 1秒后才会执行,测试报错!
  expect(callback).toHaveBeenLastCalledWith("one");
});

再增加 500 ms 即可

test("回调是否执行", () => {
  const callback = jest.fn();
  callbackFunc(callback);
  // 500毫秒后
  jest.advanceTimersByTime(500);
  // 再次500毫秒后
  jest.advanceTimersByTime(500);
  // 测试通过!
  expect(callback).toHaveBeenLastCalledWith("one");
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/152967.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【阶段三】Python机器学习10篇:机器学习项目实战:K近邻算法的基本原理、计算步骤与KNN(K近邻)分类模型

本篇的思维导图: K近邻算法(英文为K-Nearest Neighbor,因而又简称KNN算法)是非常经典的机器学习算法。 K近邻算法的基本原理 K近邻算法的原理非常简单:对于一个新样本,K近邻算法的目的就是在已有数据中寻找与它最相似的K个数据,或者说“离它最近”的K个数…

设计模式——备忘录模式

备忘录模式一、基本思想二、应用场景三、结构图四、代码五、优缺点5.1 优点5.2 缺点一、基本思想 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫…

Bilibili支持了AV1编码,关于AV1编码你知道吗?

Bilibili支持了AV1编码,关于AV1编码你知道吗? AV1编码是一种新的视频编码标准,由联合开发的开源编码器,它由英特尔、微软、谷歌、苹果、Netflix、AMD、ARM、NVIDIA和其他一些公司共同开发,旨在替代H.264和HEVC等现有的…

Word控件 Aspose.words for.NET 授权须知

Aspose.Words 是一种高级Word文档处理API,用于执行各种文档管理和操作任务。API支持生成,修改,转换,呈现和打印文档,而无需在跨平台应用程序中直接使用Microsoft Word。此外, Aspose API支持流行文件格式处…

实验 2 灰度变换与空间滤波

目录实验 2 灰度变换与空间滤波一、实验目的二、实验例题1. 灰度变换函数 imadjust2. 使用对数变换压缩动态范围。3. 直方图均衡化 histogram equalization实验 2 灰度变换与空间滤波 一、实验目的 掌握灰度变换的原理和应用。掌握对数变换、幂律变换和直方图均衡化的原理和应…

融合注意力模块SE基于轻量级yolov5s实践路面坑洼目标检测系统

在很多的项目实战中验证分析注意力机制的加入对于模型最终性能的提升发挥着积极正向的作用,在我之前的一些文章里面也做过了一些尝试,这里主要是想基于轻量级的s系列模型来开发构建路面坑洼检测系统,在模型中加入SE注意力模块,以期…

Android开发-AS学习(二)

1.5 ProgressBar常用属性描述android:max进度条的最大值android:progress进度条已完成进度值android:indeterminate如果设置为true,则进度条不精确显示进度style“?android:attr/progressBarStyleHorizontal"水平进度条MainActivity.java package c…

机试_1_暴力求解

一、枚举 判断是否可以使用枚举:分析数据量。 若时间限制在1000ms的情况下,大约可以进行10⁷的运算。 复杂度数据量O(n!)10O(2ⁿ)20O(n)200O(n)3000O(nlogn)10⁶O(n)10⁷O(√10)10⁴O(logn)>10⁰1 abc–清华大学 描述 设a、b、c均是0到9之间的数字…

Java课程设计——学生成绩管理系统

1 需求分析1.1 需求分析概述需求分析是开发软件系统的重要环节,是系统开发的第一步和基础环节。通过需求分析充分认识系统的目标、系统的各个组成部分、各部分的任务职责、工作流程、工作中使用的各种数据及数据结构、各部门的业务关系和数据流程等, 为系…

nacos的配置管理

前言 此博客对nacos的配置管理进行简单介绍,如果降配置文件放在项目中,那么每次进行修改后都要重新编译部署项目,是极其不方便的,如果将配置文件放在一个固定的位置,尽管解决了以上的问题,但是管理起来还不…

【Java寒假打卡】Java基础-File

【Java寒假打卡】Java基础-File概述-三种构造方法绝对路径和相对路径File的创建功能File的删除功能File的判断和获取功能listFile方法练习1:在当前模块下面aaa文件夹创建一个文件a.txt练习2:删除一个多级文件夹练习3:统计一个文件夹中每一种文…

UE插件和项目目录结构学习笔记

Plugins插件的二种安装方式 1、安装到虚幻引擎(推荐) 转到虚幻引擎安装位置的插件文件夹 Engine/Plugins 将解压得到的插件文件夹放入Marketplace文件夹下(如没有新建一个)。 启动虚幻引擎打开项目,菜单->编辑->…

如何在线免费将PPT转PDF格式

我们经常会遇到制作演示文稿PPT的格式,但是这种格式一般在传阅的过程中稳定性都较差,所以很多人会选择转成PDF格式,那么有没有免费的处理方式呢? 打开浏览器搜索speedpdf找到并打开在线转换工具首页,选择主页上的PPT转…

Python表白妙招,把情书写进她的照片里

前言 我的好兄弟们,2022年可算是过去了,这不马上要过年了吗 就是说,各位兄弟有对象了吗,没有的回家还要面对亲戚的各种提问 退一步来说,有心仪的人吗,如果有的话,就来看看这篇 程序员的表白小…

【Vue + Koa 前后端分离项目实战7】使用开源框架==>快速搭建后台管理系统 -- part7 前端实现最新期刊管理【增删查改】

人生没有白走的路,每一步都作数。 对应后端部分章节回顾: 【Vue Koa 前后端分离项目实战5】使用开源框架>快速搭建后台管理系统 -- part5 后端实现最新期刊列表管理【增删查改】_小白Rachel的博客-CSDN博客 效果展示: 目录 一、…

少儿Python每日一题(15):回文数

原题解答 本次的题目如下所示: 【编程实现】 回文数是指一个像14641这样“对称”的数,即:将这个数的各位数字按相反的顺序重新排列后,所得到的数和原来的数一样。请编程求不同位数数字的回文数的个数。用户输入一个正整数M(2<M<7),M作为回文数的位数。要求输出M位…

【Javascript】高阶函数,JSON

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录高阶函数箭头函数apply函数JSON高阶函数 把函数作为参数&#xff0c;或者返回一个函数&#xff…

【并发】并发锁机制-深入理解synchronized(一)

【并发】并发锁机制-深入理解synchronized&#xff08;一&#xff09; synchronized 基础篇&#xff08;使用&#xff09; 一、Java共享内存模型带来的线程安全问题 1. 代码示例 2. 运行结果 3. 问题分析 4. 临界区&#xff08;Critical Section&#xff09; 5. 竞态条件…

【Go】内存模型中的内存可见性

前言 使用go必然会使用到协程以及其他的并发操作&#xff0c;初期学习的时候&#xff0c;经常在启动协程时操作变量出现问题&#xff0c;要么就是变量没更新&#xff0c;要么就是各种崩溃&#xff0c;或者vscode报告警之类的&#xff0c;于是浅看了一下Go的内存模型&#xff0…

离散制造业ERP系统对生产物料管理有哪些帮助?

在离散制造企业生产加工过程中&#xff0c;生产物料管理是一个至关重要的环节。车间物料能不能管控好&#xff0c;影响着整个产品的品质、工作效率及制造成本的控制等。离散制造业通常需要品类、属性繁多的原材料和配套件&#xff0c;而各类物料的及时供应十分重要&#xff1b;…