Skip to content

Commit

Permalink
feat(framework): add jest 代码测试
Browse files Browse the repository at this point in the history
  • Loading branch information
NARUTOne committed Sep 2, 2020
1 parent 7eb1768 commit b3d5a96
Show file tree
Hide file tree
Showing 11 changed files with 6,868 additions and 2,831 deletions.
6 changes: 6 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ gulpfile.js

# commitizen
commitlint.config.js

# jest
jest.config.js

# build
dist
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:jest/recommended"],
};
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,7 @@ dist
types

# eslint
.eslintcache
.eslintcache

# jest
coverage
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@
## CLI

```bash
# 生成log
npm run log
# 生成changelog
npm run changelog

# 提交commit
npm run commit

# lint code
npm run lint

# test code
npm run test

# build
npm run build

Expand Down
132 changes: 122 additions & 10 deletions RECORD.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

- 使用 `git cz` 代替 `git commit` 进行符合 Angular 规范的 Commit Message 信息提交
- 代码提交之前会通过 [husky](https://github.com/typicode/husky) 配合 git hook 进行提交信息校验,一旦提交信息不符合 Angular 规范,则提交会失败
- 执行 `npm run log` 会在根目录下生成 `CHANGELOG.md` 版本日志
- 执行 `npm run changelog` 会在根目录下生成 `CHANGELOG.md` 版本日志

> 温馨提示:如果不知道什么是 CLI (命令行接口),可查看 [使用 NPM 发布和使用 CLI 工具](https://juejin.im/post/5eb89053e51d454de54db501)
Expand All @@ -41,7 +41,7 @@

- Webpack 主要用于页面应用的模块化构建,使用 Webpack 构建会增加构建库的体积,因此简单工具库的制作使用 Webpack 完全是 "杀鸡用牛刀"。
- Rollup 是一个构建工具库非常不错的轻量选择,它持有的 [Tree Shaking](https://github.com/rollup/rollup) 以及可构建输出 [ES Module](https://github.com/rollup/rollup/wiki/ES6-modules) 的特性使得它被 tsdx、microbundle 甚至 Vue 等广泛使用。
- Babel 对于 TypeScript 可使用 [@babel/preset-typescript](https://babeljs.io/docs/en/babel-preset-typescript) 去除 TypeScript 类型标记,但是不做类型编译检查,更多关于 Babel 对于 TypeScript 支持的限制可查看 [@babel/plugin-transform-typescript - Caveats](https://www.babeljs.cn/docs/babel-plugin-transform-typescript#caveats)
- Babel 对于 TypeScript 可使用 [@babel/preset-typescript](https://babeljs.io/docs/en/babel-preset-typescript) 去除 TypeScript 类型标记,但是不做类型编译检查,更多关于 Babel 对于 TypeScript 支持的限制可查看 [@babel/plugin-transform-typescript - Caveats](https://www.babeljs.cn/docs/babel-plugin-transform-typescript#caveats)[Babel 7 or TypeScript](https://kulshekhar.github.io/ts-jest/user/babel7-or-ts)
- Gulp 是一个非常轻量的构建工具,并且也是 TypeScript 官方推荐的构建工具,具体可查看 [TypeScript - Building](https://github.com/microsoft/TypeScript#building),简单的 Gulp 配置可查看 [TypeScript 中文网 - Gulp](https://www.tslang.cn/docs/handbook/gulp.html)

由于算法的函数工具库功能非常单一简单,因此采用 TypeScript 官方推荐的 Gulp 工具进行构建即可满足需求。
Expand Down Expand Up @@ -143,7 +143,7 @@ gulp.task("default", function () {
### Eslint

》 TypeScript 的代码检查工具主要有 TSLint 和 ESLint 两种。早期的 TypeScript 项目一般采用 TSLint 进行检查,TSLint 和 TypeScript 采用同样的 AST 格式进行编译,但主要问题是对于 JavaScript 生态的项目支持不够友好,因此 TypeScript 团队在 2019 年宣布全面转向 ESLint,更多关于转向 ESLint 的原因可查看:
》 TypeScript 的代码检查工具主要有 TSLint 和 ESLint 两种。早期的 TypeScript 项目一般采用 TSLint 进行检查,TSLint 和 TypeScript 采用同样的 AST 格式进行编译,但主要问题是对于 JavaScript 生态的项目支持不够友好,因此 TypeScript 团队在 2019 年宣布全面转向 ESLint,(具体可查看 TypeScript 官方仓库的 [`.eslintrc.json`](https://github.com/microsoft/TypeScript/blob/master/.eslintrc.jso) 配置), 更多关于转向 ESLint 的原因可查看:

- <https://medium.com/palantir/tslint-in-2019-1a144c2317a9>
- <https://github.com/microsoft/TypeScript/issues/30553>
Expand Down Expand Up @@ -186,7 +186,9 @@ module.exports = {
配置完成后在 `package.json` 中设置校验命令

```javascript
"lint": "eslint src",
"scripts": {
"lint": "eslint src",
}
```

此时如果在 `src` 目录下书写错误的语法,执行 `npm run lint` 就会输出错误信息:
Expand All @@ -213,6 +215,10 @@ gulpfile.js
.eslintrc.js
# commitizen
commitlint.config.js
# jest
jest.config.js
# build
dist
```

此时可以发现之前执行 `lint` 命令的错误通过插件的形式可实时在 VS Code 编辑器中进行显示。除此之外,一些简单的 ESLint 格式错误(例如 多余的`;` 等)可通过配置 Save Auto Fix 进行保存自动格式化处理。具体 VS Code 的配置可参考 [ESLint 插件](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)的文档说明,这边应该需要进行如下配置:
Expand All @@ -229,16 +235,19 @@ commitlint.config.js
除此之外,需要在构建前进行 ESLint 校验,一旦 ESLint 校验不通过则不允许进行源码的构建操作:

```javascript
"lint": "eslint src",
"lint-strict": "eslint src --max-warnings 0",
"build": "npm run lint-strict && rimraf dist types && gulp",
"scripts": {
"lint": "eslint src --max-warnings 0",
"build": "npm run lint && rimraf dist types && gulp",
}
```

需要注意正常使用 lint 校验时需要抛出所有的错误从而可以使得开发者可以逐一进行错误修复,但是在构建时需要进行严格控制,一旦 lint 抛出 warning 或者 error 则立马终止构建(详情可查看 [ESLint 退出代码](https://cn.eslint.org/docs/user-guide/command-line-interface#exit-codes))。
需要注意在构建时需要进行校验的严格控制,一旦 lint 抛出 warning 或者 error 则立马终止构建(详情可查看 [ESLint 退出代码](https://cn.eslint.org/docs/user-guide/command-line-interface#exit-codes))。

> 温馨提示:需要注意 Shell 中的 `&&``&` 是有差异的,`&&` 主要用于顺序执行,如果其中一个脚本失败退出那么整个组合脚本执行失败,`&` 主要用于并发执行,表示两个脚本同时执行。这里构建的命令需要等待 `lint` 命令执行通过才能进行,一旦 `lint` 失败那么构建命令将不再执行。
### Lint Staged

使用 [commitlint](https://commitlint.js.org/#/) 工具可以防止生成不规范的 Gig Commit Message,从而阻止用户进行 Git 代码提交。但是如果想要防止团队协作时开发者提交不符合 ESLint 规则的代码则可以通过 [lint-staged](https://github.com/okonet/lint-staged) 工具来实现。`lint-staged` 可以在用户提交代码之前(生成 Git Commit Message 信息之前)使用 ESLint 检查 Git 暂存区中的代码信息(`git add` 之后的修改代码),一旦存在 💩 一样不符合校验规则的代码,则可以终止提交行为。需要注意的是 `lint-staged` 不会检查项目的全量代码(全量使用 ESLint 校验对于较大的项目可能会是一个相对耗时的过程),而只会检查添加到 Git 暂存区中的代码。根据官方文档执行以下命令自动生成配置项信息:
使用 [commitlint](https://commitlint.js.org/#/) 工具可以防止生成不规范的 Git Commit Message,从而阻止用户进行 Git 代码提交。但是如果想要防止团队协作时开发者提交不符合 ESLint 规则的代码则可以通过 [lint-staged](https://github.com/okonet/lint-staged) 工具来实现。`lint-staged` 可以在用户提交代码之前(生成 Git Commit Message 信息之前)使用 ESLint 检查 Git 暂存区中的代码信息(`git add` 之后的修改代码),一旦存在 💩 一样不符合校验规则的代码,则可以终止提交行为。需要注意的是 `lint-staged` 不会检查项目的全量代码(全量使用 ESLint 校验对于较大的项目可能会是一个相对耗时的过程),而只会检查添加到 Git 暂存区中的代码。根据官方文档执行以下命令自动生成配置项信息:

```bash
npx mrm lint-staged
Expand All @@ -255,12 +264,115 @@ npx mrm lint-staged
"lint-staged": {
// 这里需要注意脚本的 --max-warnings 0
// 否则就算存在 warning 也不会终止提交行为
"*.ts": "npm run lint-strict"
"*.ts": "npm run lint"
}
```

此时如果代码有 💩 , 则提交时会提示错误信息且提交会强制失败

### Jest

#### Jest 配置

本项目的单元测试主要采用了 [Jest](https://jestjs.io/en/) 测试框架,需要注意 Jest 如果需要对 TypeScript 进行支持,可以通过配合 Babel 的形式,具体可查看 [Jest - Using TypeScript](https://jestjs.io/docs/en/getting-started#using-typescript),当然这会产生一些限制(具体可查看 [Babel 7 or TypeScript](https://kulshekhar.github.io/ts-jest/user/babel7-or-ts))。由于本项目没有采用 Babel 进行转译,并且希望能够完美支持类型检查,因此采用 [ts-jest](https://kulshekhar.github.io/ts-jest/user/install#customizing) 进行单元测试。按照官方教程进行依赖安装和项目初始化:

```bash
npm install --save-dev jest typescript ts-jest @types/jest
npx ts-jest config:init
```

对根目录的 `ject.config.js` 进行配置修改(注释部分是新增的配置,后续配置可能还会增加):

```javascript
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
// 输出覆盖信息文件的目录
coverageDirectory: "./coverage/",
// 覆盖信息的忽略文件模式
testPathIgnorePatterns: ["<rootDir>/node_modules/"],
// 如果测试覆盖率未达到 100%,则测试失败
// 这里可用于预防代码构建和提交
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
// 路径映射配置,具体可查看 https://kulshekhar.github.io/ts-jest/user/config/#paths-mapping
// 需要配合 TypeScript 路径映射,具体可查看:https://www.tslang.cn/docs/handbook/module-resolution.html
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
};
```

需要注意路径映射也需要配置 `tsconfig.json` 中的 `paths` 信息,同时注意将测试代码包含到 TypeScript 的编译目录中。配置完成后在 `package.json` 中配置测试命令:

```JSON
"scripts": {
"lint": "eslint src --max-warnings 0",
"test": "jest --bail --coverage",
"build": "npm run lint && npm run jest && rimraf dist types && gulp",
}
```

需要注意 Jest 中的这些配置信息(更多配置信息可查看 [Jest CLI Options](https://jestjs.io/docs/zh-Hans/cli)):

- `bail` 的配置作用相对类似于 ESLint 中的 `max-warnings`,设置为 `true` 则表明一旦发现单元测试用例错误则停止运行其余测试用例,从而可以防止运行用例过多时需要一直等待用例全部运行完毕。
- `coverage` 主要用于在当前根目录下生成 `coverage` 测试目录报告。

> 温馨提示:Jest CLI Options 中的 `findRelatedTests` 可用于配合 `pre-commit` 钩子去运行最少量的单元测试用例,可配合 `lint-staged` 实现类似于 ESLint 的作用,更多细节可查看 [`lint-staged - Use environment variables with linting commands`](https://github.com/okonet/lint-staged#use-environment-variables-with-linting-commands)
在当前根目录的 `test` 目录下新建 `greet.spec.ts` 文件,并设计以下测试代码:

```javascript
import greet from "@/greet";
describe("src/greet.ts", () => {
it("name param test", () => {
expect(greet("world")).toBe("Hello from world 1");
});
});
```

> 温馨提示:测试文件有两种放置风格,一种是新建 `test` 文件夹,然后将所有的测试代码集中在 `test` 目录下进行管理,另外一种是在各个源码文件的同级目录下新建 `__test__` 目录,进行就近测试,大部分的项目可能都会倾向采用第一种目录结构(可以随便找一些 github 上的开源项目进行查看,这里 `ts-test` 则是采用了第二种测试结构)。除此之外,需要注意的一点是 Jest 通过配置 [`testMatch`](https://jestjs.io/docs/zh-Hans/configuration#testmatch-arraystring)[`testRegex`](https://jestjs.io/docs/zh-Hans/configuration#testregex-string--arraystring) 可以使得项目识别特定格式文件作为测试文件进行运行(本项目采用默认配置可识别后缀为 `.spec` 的文件进行单元测试)。
此时可单独通过执行 `npm run test` 命令进行单元测试,这里演示执行构建命令时的单元测试(需要保证构建之前所有的单元测试用例都能通过)。如果测试失败,那么应该防止继续构建.
温馨提示:想要了解更多关于 Jest 的生态可以查看 [awesome-jest](https://github.com/jest-community/awesome-jest)

#### ESLint 支持

`src` 目录下的源码通过配置 `@typescript-eslint/eslint-plugin` 可进行推荐规则的 ESLint 校验,为了使得 `test` 目录下的测试代码能够进行符合 Jest 推荐规则的 ESLint 校验,可以通过配置 [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) 进行支持(`ts-jest` 项目就是采用了该插件进行 ESLint 校验,具体可查看配置文件 [`ts-jest/.eslintrc.js`](https://github.com/kulshekhar/ts-jest/blob/master/.eslintrc.js#L12)),这里仍然采用推荐的规则配置:

```javascript
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
// 新增推荐的 ESLint 校验规则
// 所有规则集查看:https://github.com/jest-community/eslint-plugin-jest#rules(recommended 标识表明是推荐规则)
"plugin:jest/recommended",
],
};
```

需要注意修改 `package.json` 中的 ESLint 校验范围:

```javascript
"scripts": {
// 这里对 src 和 test 目录进行 ESLint 校验
"lint": "eslint src test --max-warnings 0",
},
```

执行 `npm run lint` 进行单元测试的格式校验

> 温馨提示:如果你希望 Jest 测试的代码需要一些格式规范,那么可以查看 [eslint-plugin-jest-formatting](https://github.com/dangreenisrael/eslint-plugin-jest-formatting) 插件。
## 文档

- [Npm 官方文档](https://docs.npmjs.com/)
Expand Down
22 changes: 22 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
// 输出覆盖信息文件的目录
coverageDirectory: "./coverage/",
// 覆盖信息的忽略文件模式
testPathIgnorePatterns: ["<rootDir>/node_modules/"],
// 如果测试覆盖率未达到 100%,则测试失败
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
// 路径映射配置,具体可查看 https://kulshekhar.github.io/ts-jest/user/config/#paths-mapping
// 需要配合 TypeScript 路径映射,具体可查看:https://www.tslang.cn/docs/handbook/module-resolution.html
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
};
Loading

0 comments on commit b3d5a96

Please sign in to comment.