测试框架 Jest 实用教程

简介: 测试框架 Jest 实用教程

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

安装

cnpm i --save-dev jest

使用

  1. 在项目中的任意位置(通常单独建个名为 test 的文件夹)新建以 .test.js 为后缀的测试文件,如 expect.test.js (若项目中有 test.js 存在,请改为其他名称,JEST 会将 test.js 也视为测试文件)
  1. 在 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中可以直接点击脚本执行按钮
  1. 效果如下:

    全部绿色,即测试通过。

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

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");
});
test("回调是否执行", () => {
  const callback = jest.fn();
  callbackFunc(callback);
  // 500毫秒后
  jest.advanceTimersByTime(500);
  // 再次500毫秒后
  jest.advanceTimersByTime(500);
  // 测试通过!
  expect(callback).toHaveBeenLastCalledWith("one");
});
目录
相关文章
|
4天前
|
设计模式 前端开发 JavaScript
自动化测试框架设计原则与最佳实践####
本文深入探讨了构建高效、可维护的自动化测试框架的核心原则与策略,旨在为软件测试工程师提供一套系统性的方法指南。通过分析常见误区,结合行业案例,阐述了如何根据项目特性定制自动化策略,优化测试流程,提升测试覆盖率与执行效率。 ####
21 6
|
4天前
|
人工智能 前端开发 测试技术
探索软件测试中的自动化框架选择与优化策略####
本文深入剖析了当前主流的自动化测试框架,通过对比分析各自的优势、局限性及适用场景,为读者提供了一套系统性的选择与优化指南。文章首先概述了自动化测试的重要性及其在软件开发生命周期中的位置,接着逐一探讨了Selenium、Appium、Cypress等热门框架的特点,并通过实际案例展示了如何根据项目需求灵活选用与配置框架,以提升测试效率和质量。最后,文章还分享了若干最佳实践和未来趋势预测,旨在帮助测试工程师更好地应对复杂多变的测试环境。 ####
18 4
|
9天前
|
机器学习/深度学习 前端开发 测试技术
探索软件测试中的自动化测试框架选择与优化策略####
本文深入探讨了在当前软件开发生命周期中,自动化测试框架的选择对于提升测试效率、保障产品质量的重要性。通过分析市场上主流的自动化测试工具,如Selenium、Appium、Jest等,结合具体项目需求,提出了一套系统化的选型与优化策略。文章首先概述了自动化测试的基本原理及其在现代软件开发中的角色变迁,随后详细对比了各主流框架的功能特点、适用场景及优缺点,最后基于实际案例,阐述了如何根据项目特性量身定制自动化测试解决方案,并给出了持续集成/持续部署(CI/CD)环境下的最佳实践建议。 --- ####
|
10天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
45 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
9天前
|
测试技术 API Android开发
探索软件测试中的自动化框架选择与实践####
本文深入探讨了软件测试领域内,面对众多自动化测试框架时,如何依据项目特性和团队需求做出明智选择,并分享了实践中的有效策略与技巧。不同于传统摘要的概述方式,本文将直接以一段实践指南的形式,简述在选择自动化测试框架时应考虑的核心要素及推荐路径,旨在为读者提供即时可用的参考。 ####
|
12天前
|
缓存 测试技术 Apache
告别卡顿!Python性能测试实战教程,JMeter&Locust带你秒懂性能优化💡
告别卡顿!Python性能测试实战教程,JMeter&Locust带你秒懂性能优化💡
27 1
|
13天前
|
测试技术 Android开发 UED
探索软件测试中的自动化框架选择
【10月更文挑战第29天】 在软件开发的复杂过程中,测试环节扮演着至关重要的角色。本文将深入探讨自动化测试框架的选择,分析不同框架的特点和适用场景,旨在为软件开发团队提供决策支持。通过对比主流自动化测试工具的优势与局限,我们将揭示如何根据项目需求和团队技能来选择最合适的自动化测试解决方案。此外,文章还将讨论自动化测试实施过程中的关键考虑因素,包括成本效益分析、维护难度和扩展性等,确保读者能够全面理解自动化测试框架选择的重要性。
31 1
|
11天前
|
机器学习/深度学习 自然语言处理 物联网
探索自动化测试框架的演变与未来趋势
随着软件开发行业的蓬勃发展,软件测试作为保障软件质量的重要环节,其方法和工具也在不断进化。本文将深入探讨自动化测试框架从诞生至今的发展历程,分析当前主流框架的特点和应用场景,并预测未来的发展趋势,为软件开发团队选择合适的自动化测试解决方案提供参考。
|
7天前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
36 3
|
1月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
59 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)

热门文章

最新文章