【jest使用】

Quick Start

安装:
java 复制代码
npm install --save-dev jest

让我们开始为一个假设函数编写测试,该函数将两个数字相加。 首先,创建一个 sum.js 文件:

javascript 复制代码
function sum(a, b) {
  return a + b;
}
module.exports = sum;

然后,创建一个名为 sum.test.js 的文件。jest会自动找对应的test作为测试文件,所以我们这里也使用了.test文件名。 这将包含我们的实际测试:

javascript 复制代码
const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});
  • test方法:需要测试的代码
  • expect方法 :预期方法,就是你调用了什么方法,传递了什么参数,得到的预期是什么

将下面的配置部分添加到你的 package.json 里面:

json 复制代码
{
  "scripts": {
    "test": "jest"
  }
}

最后,运行 yarn testnpm run test ,Jest将打印下面这个消息:

bash 复制代码
PASS  ./sum.test.js
✓ adds 1 + 2 to equal 3 (5ms)

若需要每次修改控制台就会自动跑测试代码,可配置:

javascript 复制代码
{
  "scripts": {
    "test": "jest --watchAll"
  }
}
测试覆盖率生成

在jest.config.js里面配置coverageDirectory : "coverage" ,coverageDirectory为输出覆盖信息文件的目录

javascript 复制代码
const {defaults} = require('jest-config');
module.exports = {
  moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'],
  coverageDirectory : "coverage" 
};

使用npx jest --coverage即可生成一个代码测试覆盖率的说明

让jest支持ES6

由于jest不支持ES6,需要配置babel进行ES6的转换

安装依赖:

javascript 复制代码
npm install --save-dev babel-jest @babel/core @babel/preset-env

可以在工程的根目录下创建一个babel.config.js文件用于配置与你当前Node版本兼容的Babel:

javascript 复制代码
// babel.config.js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          node: 'current',
        },
      },
    ],
  ],
};

创建jest.config.js

javascript 复制代码
// jest.config.js
const {defaults} = require('jest-config');
module.exports = {
  moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx']
};
jest中的匹配器(详细可查看官网

toBe 使用 Object.is 判断是否严格相等。

toEqual 递归检查对象或数组的每个字段。

toBeNull 只匹配 null

toBeUndefined 只匹配 undefined

toBeDefined 只匹配非 undefined

toBeTruthy 只匹配真。

toBeFalsy 只匹配假。

toBeGreaterThan 实际值大于期望。

toBeGreaterThanOrEqual 实际值大于或等于期望值

toBeLessThan 实际值小于期望值。

toBeLessThanOrEqual 实际值小于或等于期望值。

toBeCloseTo 比较浮点数的值,避免误差。

toMatch 正则匹配。

toContain 判断数组中是否包含指定项。

toHaveProperty(keyPath, value) 判断对象中是否包含指定属性。

toThrow 判断是否抛出指定的异常。

toBeInstanceOf 判断对象是否是某个类的实例,底层使用 instanceof

jest进行异步测试(详细可查看官网

有一个异步请求接口fetchData,对其进行单元测试,代码如下:

javascript 复制代码
//fetchData.js
import axios from 'axios';

export const fetchData = (fn)=>{
    axios.post('接口').then((response)=>{
      fn(response.data.responseCode);
  })
}
javascript 复制代码
//fetchData.test.js
import { fetchData } from '../fetchData.js'

test('fetchData 测试',()=>{
    fetchData((res)=>{
    	expect(res).toEqual('接口返回的值');
    })
})

注意这样写是有问题的,因为方法还没有等到回调,我们的结果已经完成了,所以这时候你对于没测试完,只是方法可用,就返回了测试结果,这种结果是不保证正确的。

方法一 :使用单个参数调用 done,而不是将测试放在一个空参数的函数。 Jest会等done回调函数执行结束后,结束测试。

javascript 复制代码
//fetchData.test.js
import { fetchData } from '../fetchData.js'

test('fetchData 测试',(done)=>{
    fetchData((res)=>{
    	expect(res).toEqual(str);
        done();
    })
})

方法二 :使用Promise的方式,fetchData 不使用回调函数,而是返回一个 Promise

javascript 复制代码
//fetchData.js
export const fetchData = () => {
  return axios.post('接口');
}

返回一个promise

javascript 复制代码
//fetchData.test.js
test('fetchData 测试', () => {
  return fetchData().then(res => {
    expect(res.data.responseCode).toEqual('接口返回的值'); //res.data.responseCode 接口自定义的数据结构
})

不要忘记把 promise 作为返回值⸺如果你忘了 return 语句的话,在 fetchData 返回的这个 promise 被 resolve、then() 有机会执行之前,测试就已经被视为已经完成了。

同样的,可以通过.catch来测试异常情况

javascript 复制代码
//fetchData.test.js
test('fetchData 测试', () => {
  return fetchData().catch(err => {
    expect(err).toEqual('error'); 
})

这里需要说明一下,只有出现异常的时候才会走这个方法,若没有出现异常,就不会走这个测试方法,而Jest会默认这个用例通过了测试。因此需要使用expect.assertions(1),这个代码的意思是"断言,必须需要执行一次expect方法才可以通过测试"。

javascript 复制代码
//fetchData.test.js
test('fetchData 测试', () => {
  expect.assertion(1);
  return fetchData().catch(err => {
    expect(err).toEqual('error'); 
})

**方法三:**使用async/await方式

javascript 复制代码
//fetchData.js
export const fetchData = () => {
  return axios.post('接口');
}

返回一个promise

javascript 复制代码
//fetchData.test.js
test('fetchData 测试', async () => {
  let res = await fetchData();
  expect(res.data.responseCode).toEqual('接口返回的值');
})
jest使用Mock进行测试(详细可查看官网

jest提供了Mock方法,可以通过jest.fn()编写mock方法

javascript 复制代码
const myMock = jest.fn(res => {console.log('mock....')});

myMock();  // mock....

通过jest.mock()对已有的方法或模块进行mock

javascript 复制代码
// foo.js
export const foo = () => {
  //do something...
}

// test.js
import { foo } from './foo'
jest.mock('./foo'); //mock foo 

foo.mockImplementation(() => 42);
foo();  // 42

或者可以这样

javascript 复制代码
// foo.js
export const foo = () => {
  //do something...
}

// test.js
import { foo } from './foo'
jest.mock('./foo', () => { //mock foo 
    return {
        foo: jest.fn(() => 42);
    }
});

foo();  // 42

注: jest不支持ES6,若要使用import、export等需要进行babel转换

Q&A

1.jest.mock()模块工厂不允许引用任何范围外的变量

例如:

javascript 复制代码
import { demo } from './demo';
import { foo } from './foo';
jest.mock('./foo', () => {
	demo();
});

此时会报错

javascript 复制代码
The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: str
    Allowed objects: Array, ArrayBuffer, Atomics, BigInt, BigInt64Array, BigUint64Array, Boolean, Buffer, COUNTER_HTTP_CLIENT_REQUEST, COUNTER_HTTP_CLIENT_RESPONSE, COUNTER_HTTP_SERVER_REQUEST, COUNTER_HTTP_SERVER_RESPONSE, COUNTER_NET_SERVER_CONNECTION, COUNTER_NET_SERVER_CONNECTION_CLOSE, DTRACE_HTTP_CLIENT_REQUEST, DTRACE_HTTP_CLIENT_RESPONSE, DTRACE_HTTP_SERVER_REQUEST, DTRACE_HTTP_SERVER_RESPONSE, DTRACE_NET_SERVER_CONNECTION, DTRACE_NET_STREAM_END, DataView, Date, Error, EvalError, Float32Array, Float64Array, Function, GLOBAL, Generator, GeneratorFunction, Infinity, Int16Array, Int32Array, Int8Array, InternalError, Intl, JSON, Map, Math, NaN, Number, Object, Promise, Proxy, RangeError, ReferenceError, Reflect, RegExp, Set, SharedArrayBuffer, String, Symbol, SyntaxError, TypeError, URIError, URL, URLSearchParams, Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, WeakMap, WeakSet, WebAssembly, arguments, clearImmediate, clearInterval, clearTimeout, console, decodeURI, decodeURIComponent, encodeURI, encodeURIComponent, escape, eval, expect, global, isFinite, 
isNaN, jest, parseFloat, parseInt, process, require, root, setImmediate, setInterval, setTimeout, undefined, unescape.
    Note: This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with `mock` (case insensitive) are permitted.

解决方案:

可使用jest.requireActual()

javascript 复制代码
import { demo } from './demo';
import { foo } from './foo';
jest.mock('./foo', () => {
	const res = jest.requireActual('./demo')
    res.demo();
});

参考

https://jestjs.io/docs/zh-Hans/getting-started

相关推荐
敲代码的嘎仔16 分钟前
JavaWeb零基础学习Day6——JDBC
java·开发语言·sql·学习·spring·单元测试·maven
安冬的码畜日常4 小时前
【JUnit实战3_28】第十七章:用 JUnit 5 实测 SpringBoot 项目
spring boot·功能测试·测试工具·设计模式·单元测试·junit5
l1t5 小时前
luadbi和luasql两种lua duckdb驱动的性能对比
开发语言·单元测试·lua·c·csv·duckdb
蓝瑟7 小时前
前端测试不再难:Vite+React+Vitest单元测试完整手册
前端·react.js·单元测试
卓码软件测评17 小时前
第三方软件测试机构:【“Bug预防”比“Bug发现”更有价值:如何建立缺陷根因分析与流转机制?】
功能测试·测试工具·单元测试·测试用例·压力测试·可用性测试
l1t20 小时前
利用DeepSeek辅助修改luadbi-duckdb读取DuckDB decimal数据类型
c语言·数据库·单元测试·lua·duckdb
lang2015092820 小时前
Spring Boot日志配置完全指南
java·spring boot·单元测试
贪婪的君子2 天前
【每日一面】实现一个深拷贝函数
前端·js
安冬的码畜日常2 天前
【JUnit实战3_23】 第十四章:JUnit 5 扩展模型(Extension API)实战(上)
测试工具·junit·单元测试·jdbc·h2·extension模型·junit5扩展
慧都小项3 天前
Parasoft C/C++test如何在ARM DS-5环境中进行测试(下)
单元测试·parasoft·arm ds-5