Jest 30 快速上手:从零安装、编写并运行你的第一个测试

【免费下载链接】jest Delightful JavaScript Testing. 【免费下载链接】jest 项目地址: https://gitcode.com/gh_mirrors/je/jest

本篇指南以 Jest 30 官方文档《Getting Started》为骨架,带你从零完成 Jest 的安装、第一个测试用例的编写与运行,并延伸覆盖命令行参数、Babel 转译、webpack/Vite/Parcel 打包器协作、TypeScript 与 ESLint 集成等完整上手链路。读完本文,你将掌握一套可直接复制到真实项目中的 Jest 初始化流程,并能对照仓库中的示例与源码理解 expect/toBe 等核心机制的底层行为。文中所有示例在仓库 examples/getting-started 目录下都有完整可运行版本。

一、安装 Jest

使用你习惯的包管理器,将 Jest 安装为项目的开发依赖(devDependencies):

npm install --save-dev jest

或者使用 Yarn:

yarn add --dev jest

安装完成后,Jest 的可执行入口位于 packages/jest/bin/jest.js。从源码可以看到,该入口通过 import-local 优先使用项目本地安装的 Jest(而不是全局版本),随后将控制权交给 jest-cli——这正是"每个项目使用自己版本 Jest"的保障机制,也解释了为什么官方始终推荐以 --save-dev 方式安装,而不是全局安装。

仓库中的完整示例工程 examples/getting-started/package.json 展示了最小依赖组合:在 monorepo 中 jest 与 babel-jest 以 workspace 形式引用,配合 @babel/core、@babel/preset-env 即可运行。

二、编写第一个测试用例

1. 准备被测函数

为假设的"两数相加"函数编写测试。首先创建 sum.js:

function sum(a, b) {
  return a + b;
}
module.exports = sum;

2. 编写测试文件

接着创建 sum.test.js,文件名中 .test.js 后缀是 Jest 默认的测试文件匹配模式(testMatch),它会被 Jest 自动发现并执行:

const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

这段测试做了三件事:require 引入被测模块;用 test() 声明一个名为 "adds 1 + 2 to equal 3" 的测试用例;在断言中用 expect 包装实际值、以 toBe 匹配器断言它严格等于期望值 3。

3. 在 package.json 中添加 npm script

{
  "scripts": {
    "test": "jest"
  }
}

4. 运行并查看输出

执行 yarn test 或 npm test,Jest 会打印如下信息:

PASS  ./sum.test.js
✓ adds 1 + 2 to equal 3 (5ms)

PASS 表示测试通过,✓ 及耗时标记了单个用例的结果。至此,你就成功用 Jest 写下了第一个测试。

5. 理解 expect 与 toBe 的底层实现

本测试用到了 expect 与 toBe 两个 API。toBe 的语义是"两个值完全相同",其实现位于 packages/expect/src/matchers.ts 的 matchers 表中(toBe(received, expected),约第 74~104 行),核心逻辑只有一行:

const pass = Object.is(received, expected);

也就是说 toBe 基于 Object.is 判断严格相等,这与 === 基本等价,但能正确处理 NaN === NaN 这类边缘情况。Jest 在失败时还会做额外检查:如果值在"深比较"下相等但引用不同(例如两个内容相同的对象),错误信息中会给出"请改用 toEqual"的提示,这一贴心设计同样来自 matchers.ts 中的 deepEqualityName 逻辑。

如果要比较对象或数组的内容(而非引用),应使用 toEqual,它会在 packages/expect/src/matchers.ts 中通过 equals 与自定义比较器递归检查每个字段;更严格的 toStrictEqual 还会校验 undefined 属性、稀疏数组、类型不匹配等细节。更多匹配器(真值判断、数值比较、字符串、数组、异常抛出等)详见 Using Matchers 与 ExpectAPI.md。

三、从命令行运行 Jest

除了通过 npm script 运行,你还可以直接从命令行调用 jest(前提是它在你的 PATH 中,例如通过 yarn global add jest 或 npm install jest --global 全局安装),并携带各种实用选项。

例如,运行所有文件名匹配 my-test 的测试、使用 config.json 作为配置文件、并在运行结束后弹出原生系统通知:

jest my-test --notify --config=config.json

命令行还有很多常用组合,完整清单见 Jest CLI Options,下面摘录高频用法:

命令作用
jest运行全部测试(默认行为)
jest my-test 或 jest path/to/my-test.js只运行匹配指定模式或文件名的测试
jest -o只运行与 git/hg 中已变更文件相关的测试
jest --findRelatedTests a.js b.js运行与 a.js、b.js 相关的测试
jest -t name-of-spec按 describe/test 中的名称过滤用例
jest --watch / jest --watchAll进入监听模式(前者默认只跑变更相关测试,后者跑全部)

需要特别说明的是:

  • 通过包管理器运行时,参数直接跟在 npm test -- 之后即可透传给 Jest,例如 npm test -- -u -t="ColorPicker",-u 表示更新快照,-t 表示按用例名过滤。
  • Jest 同时支持驼峰与连字符两种参数写法且效果相同,如 jest --collect-coverage 与 jest --collectCoverage 等价,也可以混合使用。
  • CLI.md 开头明确指出:每一个 Configuration 中的配置项都可以通过命令行参数指定,CLI 参数优先级高于配置文件,这是排查"为什么行为与配置不符"时的重要线索。

四、生成基础配置文件

如果不想手动维护配置文件,可以基于项目情况交互式生成:

npm init jest@latest

(Yarn 用户使用 yarn create jest@latest。)运行后 Jest 会询问几个问题,并根据你的项目(模块类型、TypeScript 与否等)生成一份带简短注释的基础配置文件。该功能在仓库中由 packages/create-jest 包实现,生成内容覆盖 testEnvironment、transform 等高频选项,适合作为后续定制配置的起点。

五、使用 Babel 转译现代语法

当项目使用 ES Modules、JSX 或尚未被 Node 原生支持的语法时,需要借助 Babel 让 Jest 能读懂测试代码。

1. 安装依赖

npm install --save-dev babel-jest @babel/core @babel/preset-env

其中 babel-jest 是 Jest 官方的 Babel 桥接包(源码见 packages/babel-jest),它负责调用 Babel 完成代码转换;@babel/preset-env 根据目标环境编译语法。使用 Yarn 时对应 yarn add --dev babel-jest @babel/core @babel/preset-env。

2. 编写 babel.config.js

在项目根目录创建 babel.config.js,让 Babel 针对当前版本的 Node 进行编译:

module.exports = {
  presets: [['@babel/preset-env', {targets: {node: 'current'}}]],
};

targets: {node: 'current'} 告诉 preset-env 只需兼容当前运行时的 Node 版本,从而只做必要的转译,输出更精简。理想的 Babel 配置取决于你的项目情况(是否用 React、是否发布到浏览器等),建议参考 Babel 官方文档按需裁剪。

3. 让 Babel 配置感知 Jest 环境

Jest 在运行时会做一件重要的事:如果 process.env.NODE_ENV 尚未被设置,它会将其设为 'test'。你的 Babel 配置可以利用这一点,按需启用仅测试阶段需要的编译插件:

module.exports = api => {
  const isTest = api.env('test');
  // 可以根据 isTest 决定启用哪些 presets 和 plugins

  return {
    // ...
  };
};

api.env('test') 在 Jest 环境下会返回 true(因为它把 NODE_ENV 设为 'test'),从而让你把"测试专用编译"与"生产编译"优雅地区分开。

4. 按需关闭自动转译

需要注意:安装 Jest 时会自动安装 babel-jest,并且只要项目里存在 Babel 配置,babel-jest 就会自动参与文件转换。如果出于某些原因(例如你的代码本就不需要转译)希望关闭这一行为,可以显式清空 transform 配置:

module.exports = {
  transform: {},
};

六、与打包器协同工作

大多数场景下,Jest 与各类打包器协作无需特殊配置——唯一的例外是:如果你使用了会"生成文件"或"自定义文件解析规则"的插件/配置,才需要额外处理。

使用 webpack

Jest 可用于以 webpack 管理资源、样式与编译的项目。webpack 相比其他工具会带来一些独特挑战(如 resolve.alias、CSS/静态资源模块、DefinePlugin 注入的全局常量等),这些都需要在 Jest 的 moduleNameMapper、transform 中做镜像配置。完整的 webpack 对接指南请参考 webpack 专项指南。

使用 Vite

Jest 官方不推荐与 Vite 搭配使用,原因是 Jest 与 Vite 的插件系统存在不兼容。虽然有第三方库 vite-jest 提供了集成示例,但该库只兼容不晚于 2.4.2 的 Vite 版本,使用范围受限。一个常用的替代方案是 Vitest——它的 API 与 Jest 兼容,如果项目基于 Vite,可以平滑迁移测试代码。

使用 Parcel

Jest 同样可用于以 parcel-bundler 管理资源、样式与编译的项目,与 webpack 场景类似。Parcel 号称零配置,接入 Jest 时也无需额外配置,直接参考 Parcel 官方文档开始使用即可。

七、在 TypeScript 项目中测试

Jest 本身不解析 TypeScript,需要借助转译工具,共有两条主流路线。

路线一:通过 Babel

前提是已按上文"使用 Babel"一节完成配置。接着安装 TypeScript 预设:

npm install --save-dev @babel/preset-typescript

并将其追加到 babel.config.js 的 presets 列表:

module.exports = {
  presets: [
    ['@babel/preset-env', {targets: {node: 'current'}}],
    '@babel/preset-typescript',
  ],
};

注意此路线的局限:Babel 对 TypeScript 的支持是纯"转译"(strip types),不做类型检查,因此 Jest 运行时不会校验你的类型错误。若需要类型检查,可改用 ts-jest,或在构建流程中单独运行 TypeScript 编译器 tsc 作为补充。

路线二:通过 ts-jest

ts-jest 是带 source map 支持的 TypeScript 预处理器,让 Jest 能直接测试 TypeScript 项目:

npm install --save-dev ts-jest

为了让它生效,通常还需要在 Jest 配置文件(如 jest.config.js)中把 transform 指向 ts-jest,官方安装文档给出了对应的配置片段,例如:

module.exports = {
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
};

类型定义:两种方式

在 TypeScript 测试文件中使用 Jest 全局 API 时,有两种方式获得类型提示:

方式一:官方 @jest/globals(推荐)

Jest 自带的类型定义会随 Jest 版本同步更新。安装:

npm install --save-dev @jest/globals

然后在测试文件中显式导入所需 API:

import {describe, expect, test} from '@jest/globals';
import {sum} from './sum';

describe('sum module', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
});

@jest/globals 的源码与类型声明位于 packages/jest-globals,它对外暴露 describe、test、expect、jest 等全套 API。其附加用法(如 describe.each/test.each 的 TypeScript 用法、mock 函数的类型标注)可进一步查阅 GlobalAPI.md 与 MockFunctionAPI.md。

方式二:第三方 @types/jest

另一种选择是安装 @types/jest:

npm install --save-dev @types/jest

它提供 Jest 全局变量(test、expect 等)的类型声明,无需手动导入即可使用。注意该包由 DefinitelyTyped 社区维护,属于第三方类型定义,可能存在"版本滞后于最新 Jest 特性"的情况,因此应尽量让 @types/jest 与所用 Jest 版本号对齐,例如使用 Jest 27.4.0 时,安装 27.4.x 的 @types/jest 最为稳妥。

八、与 ESLint 集成

Jest 可以无缝配合 ESLint 使用。前提是:只要在测试文件中使用 Jest 全局辅助函数(describe、it、test、expect 等)之前,先从 @jest/globals 导入它们,ESLint 就不会因为不认识这些全局变量而报 no-undef 错误。

如果不想在每个文件里写导入,可以把 jest 环境注册到 ESLint 配置中,让这些全局变量对所有测试文件可见:

import {defineConfig} from 'eslint/config';
import globals from 'globals';

export default defineConfig([
  {
    files: ['**/*.js'],
    languageOptions: {
      globals: {
        ...globals.jest,
      },
    },
    rules: {
      'no-unused-vars': 'warn',
      'no-undef': 'warn',
    },
  },
]);

或者使用社区维护的 eslint-plugin-jest 插件,在 overrides 中针对测试目录开启 jest/globals 环境,效果类似:

{
  "overrides": [
    {
      "files": ["tests/**/*"],
      "plugins": ["jest"],
      "env": {
        "jest/globals": true
      }
    }
  ]
}

九、下一步学习路径

从"第一个通过的测试"出发,你可以沿着以下官方文档继续深入:

  • Using Matchers:toBe/toEqual 之外的全部内置匹配器,覆盖真值、数值、字符串、数组、对象、异常等断言场景;
  • Jest CLI Options:完整命令行参数参考(watch、coverage、snapshot、并行度等);
  • Configuration:全部配置项详解,与 CLI 参数一一对应;
  • GlobalAPI.md 与 MockFunctionAPI.md:全局 API 与 mock 函数的 TypeScript 用法;
  • Webpack.md:webpack 项目的完整 Jest 接入指南。

同时,examples/getting-started 目录中的 sum.js、sum.test.js 与 package.json 是本文所有示例的仓库内可运行版本;若想深入 expect/toBe 的实现细节,直接阅读 packages/expect/src/matchers.ts 的 toBe、toEqual、toStrictEqual 定义,即可看清 Object.is、深比较与错误提示增强的完整实现逻辑。

【免费下载链接】jest Delightful JavaScript Testing. 【免费下载链接】jest 项目地址: https://gitcode.com/gh_mirrors/je/jest

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐