jest手动模拟&configuring&cli

本节内容大纲:

  • 手动模拟
    • 手动模拟自定义模块
    • 测试第三方模块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
verbose使用区别
// 打印jest的配置信息并推出
jest --showConfig

// 从指定路径中找到的一个或多个项目运行测试
// 但是这个貌似不太好用,因为当我们加上了projects之后,项目中配置的路径别名就无法正确找到外部文件的引入了
jest --projects '路径地址'

因此我们日常工作中,可能会这样配置

{
    ...
    "scripts": {
        "test-dev": "jest",
        "test": "test --ci --runInBand --silent",
        "coverage": "jest --coverage --silent"
    }
}

看都看到这里了,帮帮忙点赞支持一波~

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容