# 官网

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

# 慢速上手

慢点阅读文档,避免犯错

  • 安装:之后会用到 jest 命令,建议全局安装
npm install jest --global
# 非全局安装
npm install --save-dev jest
  • 编写被测试文件和测试文件
// src/api/sum-js.js
module.exports = function sum(a, b) {
    return a + b;
}
// test/sum-js.test.js
const sum = require('../src/api/sum.js')
test('add 1 + 2 = 3', () => {
    expect(sum(1, 2)).toBe(3);
});
  • 配置 package.json
{
  "scripts": {
    "test": "jest"
  }
}

然后执行 npm dev test 即可测试。

但是有些项目使用的是 typescript ,对这种进行测试,步骤如下:

  • 安装 jest ,之前写了,就不再写了
  • 生成基础配置文件:
jest --init
  • 安装 babel
npm install --save-dev babel-jest @babel/core @babel/preset-env
  • 配置 babel.config.js :这个需要注意,检查以下 package.json 是否有 type: module 。有就表示使用的是 ECMAScript 模块 (ESM) 来导入和导出模块。没有使用的就是 CJS
module.exports = {
  presets: [
    ['@babel/preset-env', {targets: {node: 'current'}}],
    '@babel/preset-typescript',
  ],
};

这种写法对于 CJS 是正确的,如果是在 ESM 运行环境,就要显示地将文件后缀改为 cjs 才不会报错。

建议使用 babel.config.json 格式,ESM 和 CJS 都可以使用:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "current"
        }
      }
    ],
    "@babel/preset-typescript"
  ]
}

# 配置选项

这些配置都在 jest.config.ts 或者 jest.config.js

# moduleFileExtensions

用于指定 Jest 在查找测试文件时应该搜索哪些文件扩展名。默认情况下,Jest 会搜索以 .js、.jsx、.ts 和 .tsx 结尾的文件。

moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node']

# testMatch

用于指定 Jest 查找测试文件,默认情况下,Jest 会在项目根目录下查找所有以 .test.js.spec.js.test.ts.spec.ts 结尾的文件,并运行其中的测试用例。

即默认值: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ]

# coverageDirectory

Default: undefined

The directory where Jest should output its coverage files.

# preset

用作 Jest 配置基础的预设。 预设应该指向一个 npm 模块,该模块在根目录下有一个 jest-preset.json、jest-preset.js、jest-preset.cjs 或 jest-preset.mjs 文件。

# transform

Default: {"\\.[jt]sx?$": "babel-jest"}

Jest 会在项目里以原始的 JavaScript 执行,所以如果你用了一些 Node 环境不支持的语法 (比如 JSX, TypeScript, Vue 模板语法),那就要把你的代码转译成原始的 JavaScript,这就跟你在构建浏览器前端代码时要做的转译工作一样。Jest 提供 transform 配置来支持 Js 转译。

转译器(Transformer) 是一个能提供转译源代码能力的模块。 举个例子,假如你想在你的业务和测试代码中使用一些还没被 Node 支持的新语言特性,你可以引入一个代码预处理器来将新版本的 JavaScript 转译成当前支持的版本。

Jest 已经内置了一个现成的转译器 babel-jest 。 它会加载你项目的 Babel 配置,然后转译所有能正确匹配 /\.[jt]sx?$/ 正则表达式的文件 (也即所有 .js.jsx.ts.tsx 文件)。

如果你想将它和其他代码预处理器一起使用,那必须要显式地引入默认的 babel-jest 转译器:

"transform": {
  "\\.[jt]sx?$": "babel-jest",
  "\\.css$": "some-css-transformer",
}

# transformIgnorePatterns

转换器需要忽略的文件,Default: ["/node_modules/", "\\.pnp\\.[^\\\/]+$"]

# moduleNameMapper

通常,静态文件在测试中无足轻重,因为我们可以安全地 mock 他们。 然而, 如果你使用 CSS 模块,那么最好是给你的类名查找模拟一个代理。

"moduleNameMapper": {
    "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
    "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
}

所有 mock 文件本身:

// __mocks__/styleMock.js
module.exports = {};
// __mocks__/fileMock.js
module.exports = 'test-file-stub';

# collectCoverage

默认: false

指出是否收集测试时的覆盖率信息。 由于要带上覆盖率搜集语句重新访问所有执行过的文件,这可能会让你的测试执行速度被明显减慢。为 false 时也不会生成 coverageDirectory 指定的目录。

# api 使用

# 匹配器

testit 是一样的,只是与可读性有关。它们需要和匹配器 expect 一起使用

test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});

匹配器提供了很多个精准 api,关于 expect 适用于哪些对象,使用的时候查官网即可:https://jestjs.io/zh-Hans/docs/using-matchers

# 测试异步代码

异步方式运行当前代码,jest 需要知道当前它测试的代码是否已完成。jest 由以下两个方法处理该情况。

  • promise:测试一个异步方法,可以将匹配器放在返回的 promise 对象中
test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});
  • async/await:在传递给 test 函数前加上 async
test('the data is peanut butter', async () => {
  const data = await fetchData();
  expect(data).toBe('peanut butter');
});

# mock 函数

mock 函数允许你测试代码之间的连接,现在假设要测试 foreach 内部实现:

export function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index]);
  }
}

为了测试该函数,我们可以使用一个 mock 函数,然后检查 mock 函数的状态来确保回调函数如期调用:

const forEach = require('./forEach');
const mockCallback = jest.fn(x => 42 + x);
test('foreach mock function', () => {
    forEach([0,1], mockCallback);
    
    // 匹配 mock 函数被调用两次
    expect(mockCallback.mock.calls).toHaveLength(2);
    
    // 第一个 mock 调用的第一个参数是 0
    expect(mockCallback.mock.calls[0][0]).toBe(0);
})

jest.fn() 是创建 Mock 函数最简单的方式,如果没有定义函数内部的实现, jest.fn() 会返回 undefined 作为返回值。

通过 .mock ,我们还可以得到更多的信息:

  • .mock.results[0].value :第一次调用的返回值
  • .mock.contexts[0] :该函数用特定的 this 上下文调用
  • .mock.instances.length :该函数被实例化了几次

# 挂载

在测试 vue 文件里的方法(export default)时,我们需要将其挂载( mount 或者 shallowMount ),获得组件包装器对象 wrapper ,该对象提供了方法来访问和操作包装的组件:

  • 访问和断言组件的属性、状态和计算属性。
  • 触发组件的事件,以模拟用户交互。
  • 查找和断言组件中的元素和子组件。
  • 修改组件的属性和状态。

最基本的就是通过 wrapper.vm 来访问组件实例

# husky

官网文档:https://typicode.github.io/husky/

# 自动安装

npx husky-init && npm install

它会:

  • 添加 preparepackage.json
  • 创建 pre-commit 勾子
  • 配置 git 勾子路径

# 手动安装

1. 安装 husky

npm install husky --save-dev

2. 启用 git 勾子(会产生.husky 目录):

npx husky install

3. 编辑 package.json 用于安装后自动启用勾子,该命令会自动修改 package.json 文件:

npm pkg set scripts.prepare="husky install"

4. 增加一个勾子:

npx husky add .husky/pre-commit "npm test"
# 如果是其他需要执行的命令,自己修改即可
git add .husky/pre-commit

5. 尝试做一次提交:

git commit -m "Keep calm and commit"

勾子有些文件是需要放在 .git 目录下,会默认 .git 目录和 package.json 同级,如果不同级,就会报找不到 package.json 或者 npm 的 help 信息。

假设有一个独立的项目, package.json 相对于仓库的路径为: project/package.json.git 目录相对于仓库的路径就是 ./.git 。现在我们的终端就位于 /project 。现在重新开始操作:

1. 安装 husky

npm install husky --save-dev

2. 修改 package.json 文件:

npm set-script prepare "cd .. && husky install project/.husky"

如果执行不成功,那么就自己手动修改即可。

3. 执行安装,这一步如果显示 husky 找不到该命令,说明你的 husky 版本太低了:此时就会在 project 目录下创建

npm run prepare

4. 增加勾子(如果无法成功,可以使用 npx,后面引号的部分先不加,文件创建好后手动去加):

npm husky add .husky/pre-commit "cd project && npm run test"

之后 commit 就会执行对应的命令。

# 工作改错

背景描述:从 git 上将项目拉一下,然后 npm install ,安装相关依赖。然后执行 jest 测试,需要测试的文件是 ts 文件,模块为 ES6 。测试结果就是跑不通,报的错如下描述。

总结:估计是上一个测试同学没有把他的 package.json 传到 git 上,我相关的依赖在 install 就缺失了。

吐槽:上一个测试开发代码能力真的低。

  • 错误 1:找不到 ts 后缀的测试文件,在 jest.config.js 中的 moduleFileExtensions 中加入 ts
  • 错误 2:路径找不到,因为被测试文件是 ts ,这里 import 时是 js ,去掉后缀即可
Configuration error:
Could not locate module @/utils/index.js mapped as:
F:\Code\front-end\ssc\business-platform-basic-frontend\business-platform-basic-main\src\utils/index.js.
  • 错误 3:更正错误 2 后遇到了错误 3,显示为 Jest 无法解析 exportimport 。这是因为 Jest 不支持 es 模块,需要通过 babel 来处理。
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

安装 babel 以及配置 babel.config.ts/js ,这部分请参考上面的慢速上手先别急着装,继续看下面的话)。如果有需要,还要配置 jest.config.js 中的 transform

transform: {
       '^.+\\.jsx?$|^.+\\.tsx?$': 'babel-jest'
  },

如果你的项目版本太老了,安装 @babel/preset-typescript 失败了,要么把该 preset 版本降低,或者安装 @babel/plugin-transform-typescript ,他俩的关系是: preset 包含了 plugin 。该插件的功能为:添加对 TypeScript 编程语言使用的类型语法的支持。

npm install --save-dev @babel/plugin-transform-typescript

然后在 babel.config.json 修改:

{
  "plugins": ["@babel/plugin-transform-typescript"]
}

这部分请看 babel 官网