本节内容大纲:
- 手动模拟
- 手动模拟自定义模块
- 测试第三方模块lodash
- 手动模拟axios
- configuring
- option
- cli
- 从命令行运行
- 运行
- jest命令支持驼峰和破折号
- reference
手动模拟
手动模拟用于用模拟数据存根功能。例如,您可能不想访问网站或数据库等远程资源,而是创建一个允许您使用虚假数据的手动模拟。这可确保您的测试快速而稳定。
这里分享三个手动模拟:
- 手动模拟自定义模块
- 测试第三方模块lodash
- 手动模拟axios
附带规则(谨记):
__mocks__/手动模拟是通过在紧邻模块的子目录中编写模块来定义的。例如,要模拟目录user中调用的模块models,创建一个名为的文件user.js并将其放入models/mocks目录中。请注意,mocks文件夹区分大小写,因此MOCKS在某些系统上命名目录会中断。

手动模拟自定义模块
新建文件__tests__/models/user.ts
// eslint-disable-next-line prefer-const
const utils = {
count: 12,
getCount: (num: number) => {
return num * 20
}
}
export default utils;
新建文件__tests__/models/mocks/user.ts
const utils = jest.createMockFromModule<typeof import('../user')>('../user');
utils.default.getCount = jest.fn(x => x*10)
utils.default.count = 30;
export default utils.default;
新建测试__tests__/manual/user.test.tsx
import utils from "./models/user";
// import { default as utils } from './models/user'; // 也可以这样导出
jest.mock('./models/user')
describe('test manual mock',() => {
test('if original user model', () => {
// 此时拿到的数据是__mocks__/user.ts重新赋值的内容
// 如果__mocks__/user.ts 中导出不是utils.default,而是utils,那么这里的utils虽然有值,但是测试这个文件拿到的utils.default为undefined
console.log(utils,'---')
expect(utils.getCount(10)).toBe(100);
});
})
注意点:
- 由于eslint对于没有改变的数据强行使用const,因此需要忽略这条检查:eslint-disable-next-line
- __mocks__中导入的对象,实际上数据保存在一个default的属性上,并且导出也必须是导出这个defualt。因为在测试文件中无法通过utils.default进行访问
- createMockFromModule注意他的ts写法和js写法的不同,参考文档:https://www.jestjs.cn/docs/jest-object#jestcreatemockfrommodulemodulename
测试第三方模块lodash
测试第三方模块比测试自定义模块更简单,我们首先要建立一个__mocks__文件夹,然后在这个文件夹里面对第三方模块进行重写;然后新建测试导入lodash,并且不需要使用jes.mock('lodash')
这里前面已经建立了__mocks__,这里只需要在新建文件
__tests__/models/mocks/lodash.ts
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const lodash = jest.createMockFromModule<any>('lodash');
lodash.head = () => 5;
export default lodash;
新建测试__tests__/manual/testLodash.test.tsx
import lodash from 'lodash';
describe('test manual',() => {
test('if lodash head is mocked', () => {
expect(lodash.head([2, 3])).toBe(5);
});
})
注意事项:
- mocks文件默认建立在node_module相邻的目录中,但是我们项目中的jest.config.js配置了root:"/src/",因此mocks只能放在src及以下的子文件夹内
- 注意ts的写法,即需要给lodash返回一个类型,我这里直接写了一个any
手动模拟axios
测试不会发起网络请求,如果我们执意执行的话,会提示我们报错
console.errorError: Error: connect ECONNREFUSED ::1:80at Object.dispatchError (D:\workFile\react-demo\20230505\jest-learn\node_modules\jsdom\lib\jsdom\living\xhr\xhr-utils.js:63:19)
因此我们有两种方法:一个是前面聊到的章节=>模拟异步/模拟axios,如果忘记了可以返回去查看,另一个方法便是自定义模拟。经过前面对自定义模拟的了解,我们知道,如果在一个ts文件旁边放一个__mocks__,并在这个文件夹里面新建一个和__mocks__相邻的同文件名,那么这个文件将会处于自定义状态,我们在测试的时候就只需要jest.mock()指定这个__mocks__相邻文件的路径,就能达到无需发起网络请求,而自定义返回数据的效果。
因此我们新建文件api/__mocks__/home.ts,这样他就是模拟了api/home.ts文件:
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const home_api: any = jest.createMockFromModule<typeof import('../home')>('../home')
home_api.default.getHomeHead = () => {
console.log('---')
return new Promise((resolve) => {
resolve({
time: 1,
title: "news over world"
})
})
}
export default home_api.default;
新建测试__tests__/manual/testAxios.test.tsx
import utils from '../../api/home';
// 一定要有,如果没有的话,那么就变成了单纯的方法引入了
jest.mock('../../api/home');
describe('test manual',() => {
it('test manual axios',async () => {
console.log(utils,'====')
const res = await utils.getHomeHead();
console.log(res,'jjjj')
})
})
如果这样跑,大家会出现一个怪异的问题,那就是导入utils竟然包含一个default,这是因为我们我们文件api/__mocks__/home.ts是默认导出的,因此得改成导出home_api.default,而不是home_api;改完之后还是不行,因为home.ts和__mocks__/home.ts在行为上必须一致,即前者默认导出,那么后者也需要默认导出一次,而我们之前home.ts是单独导出的,因此需要改一下:
修改api/home.ts
...
export const getHomeData = async () => {...}
export const getHomeHead = async () => {...}
export const request = (url: string) => {...}
export default {
request,
getHomeHead,
getHomeData
}
这样,我们才能完美的通过测试。
configuring
Jest 的理念是默认情况下工作得很好,但有时您只需要更多的配置能力。他保存在一个.config.js|ts|mjs|cjs|json文件当中,可以直接导出一个对象或者一个函数。这里我主要讲解一些平时我们可能会用到的,后续讲解react组件测试的时候我会再加上配置运行的方法,同时以下每个属性的介绍建议过一遍。
export default {
verbose: true,
}
// or
import type {Config} from 'jest';
export default async (): Promise<Config> => {
return {
verbose: true,
};
};
options
jest有很多配置,但是我们并不是需要全部关心,因为工作当中很少会运用到,我们只需要知道一些常用的就行。这里我以我单测遇到的一些配置进行讲解,当看完我这部分的配置之后,相信大家会对单测配置有一个基本了解,从而让我们去看工作中的jest配置,能够从容不迫。退一步讲,如果还有不明确的,可以查询官网:
https://jestjs.io/docs/configuration#reference
直接上配置(需要注意以下配置,最好是记忆,其余的基本上用不到):
export default {
// 用作配置jest的基础字段,这里我们如果是ts的项目,可以使用ts-jest,但是前提得先安装他
preset: 'ts-jest',
// 可以参考“从命令行运行”内容,用于搜索文件目录路径的跟页路径,一般设置成如下,这样我们
roots: ['<rootDir>/src/'],
// verbose用于是否开启报告每个单测结果,默认是开启一个线池,可以参考“参考reference”的对比图片
verbose: false,
// jest 默认测试环境是node,如果是创建的web应用,那么就使用jsdom
testEnvironment: 'jsdom',
// 输出覆盖范围的文件目录,即生成覆盖率的文件夹名称
coverageDirectory: 'coverage',
// 每次测试前自动清除模拟调用、实例和结果
clearMocks: true,
// 指示应使用哪个提供程序来检测覆盖范围的代码
coverageProvider: "v8",
// 从正则表达式到模块名称或到允许存根资源的模块名称数组的映射
// 我们换一种方式去理解:就是供我们配置测试路径的映射和解析资源的映射,资源包括css和img等,其中图片和css等资源我们可以,此外
// 一些第三方的使用如果test无法通过,也可以在这里配置去忽略他
// 安装identity-obj-proxy,以下为写法参考,或者你可以参考“测试React组件”这一节内容
// 这里面的$1表示正则里面匹配组的序号
moduleNameMapper: {
// 处理css等资源
'\\.(css|scss|less)': 'identity-obj-proxy',
// 处理图片等资源
'\\.(jpg|png|webp|gig|svg|mp4|webm|mp3|m4a|aac)$': 'identity-obj-proxy',
// 配置jest测试环境,访问组件的通配符,但是如果只是访问ts文件,那么则无需要配置,只需要配置vite和tsconfig的配置即可
'^~/(.*)': '<rootDir>/src/$1'
},
// 收集需要测试的 文件,支持匹配正则
collectCoverageFrom:[],
// 收集需要测试的文件有哪些,支持匹配正则
testMatch:[]
}
cli
命令行运行器jest有许多有用的选项。您可以运行jest --help以查看所有可用选项。下面显示的许多选项也可以一起使用,以完全按照您想要的方式运行测试。Jest 的每个配置选项也可以通过 CLI 指定。说人话,就是为了看懂别人的写法,因为自己很多时候直接就jest了。。。
从命令行运行
查看: jest --help
运行
- jest 直接运行
- jest 路径名 => 运行的路径是基于jest.config.js中的roots
例如:
roots: [
"<rootDir>/src/_test/common"
],
那么我们执行jest的时候执行的是src/_test/common下面的test内容,同时我们在运行的时候执行jest common/路径名就相当于是匹配到了/src/_test/common/路径名
运行与更改文件相关的测试: jest -o
他运行的测试文件是运行您本地没有commit的jest文件,如果commit之后,jest -o执行无效,并且会提示我们直接执行jest或者执行jest --all
jest命令支持驼峰和破折号
这就说明下面两个效果是一样,都表明更新快照,这个在测试react组件,组件内容发生改变的时候使用
jest --update-snapshot
jest --updateSnapshot
参考reference
jest的命令行也有很多,但是绝大部分我们可能并不需要去关注,这里我以我自己常用的一些作为讲解,如果工作中大家遇到了其它的配置,可以参考官网进行查阅:
https://jestjs.io/docs/cli
// 打印jest配置信息并运行,他同jest --showConfig有相同点,不同的是debug还能继续测试,而showConfig不会进行测试
jest --debug
// 依次执行test文件,默认是创建一个测试的工作池
jest --runInBand 等同于jest -i
// 忽略测试文件中的打印信息,这个只需要了解,因为测试中的打印我们还是需要的
jest --silent => jest -i --silent 实现单个测试+忽略测试文件打印
// 重新记录在此测试运行期间失败的每个快照
jest --updateSnapshot 等同于jest -u
// 打印jest版本
jest --version
// 按照单个文件测试结果,输出打印,对于调试比较有用,因为你可以发现是那块出了问题
// 下面是jest和jest --verbose不同输出打印的截图
jest --verbose

// 打印jest的配置信息并推出
jest --showConfig
// 从指定路径中找到的一个或多个项目运行测试
// 但是这个貌似不太好用,因为当我们加上了projects之后,项目中配置的路径别名就无法正确找到外部文件的引入了
jest --projects '路径地址'
因此我们日常工作中,可能会这样配置
{
...
"scripts": {
"test-dev": "jest",
"test": "test --ci --runInBand --silent",
"coverage": "jest --coverage --silent"
}
}
看都看到这里了,帮帮忙点赞支持一波~