一、前言
本文基于开源项目:
对于单元测试,可能小伙伴们的第一反应都是“难”,能不写一般就不去写了。广东靓仔也觉得写单元测试是个有挑战性,且有难度的任务,但广东靓仔觉得大家可以尽量去尝试写一写单元测试,在bug减少的同时,项目的质量也有很大的提升,对个人而言一定能提升我们自己的能力。 本文我们一起来看看Jest,Jest现在已经更新到了28~
二、what Jest
Jest 是一个令人愉快的 JavaScript 测试框架,专注于"简洁明快"。这些项目都在使用 Jest:Babel、 TypeScript、 Node、 React、 Angular、 Vue 等等!
特点:
👩🏻💻 零配置:Jest 的目标是在大部分 JavaScript 项目上实现开箱即用, 无需配置。📸快照测试:能够轻松追踪大型对象的测试。快照可以与测试代码放在一起,也可以集成进代码行内。🏃🏽 隔离:测试程序拥有自己独立的进程 以最大限度地提高性能。👩🏻💻 优秀的api:从 it 到 expect - Jest 将整个工具包放在同一个 地方。好书写、好维护、非常方便。
三、入门
安装 Jest:npm / yarn
npm install --save-dev jest # or yarn add --dev jes
一般在选中哪个版本的时候,广东靓仔建议使用稳定的版本即可,不一定要最新。
(@27版本)初始化【@28可以省略这一步】
npx jest --init
执行完后能看到如下文件(翻译了一下):
export default { // 测试中所有导入的模块都应该自动模拟 // automock: false, // `n` 次失败后停止运行测试 // bail: 0, // Jest 应该存储其缓存的依赖信息的目录 // 每次测试前自动清除模拟调用、实例、上下文和结果 // 开启覆盖率 clearMocks: true, // 指示是否应在执行测试时收集覆盖率信息 collectCoverage: true, // 一组 glob 模式,指示应为其收集覆盖信息的一组文件 // collectCoverageFrom: undefined, // Jest 应该输出其覆盖文件的目录 coverageDirectory: "coverage", // 用于跳过覆盖收集的正则表达式模式字符串数组 // coveragePathIgnorePatterns: [ // "\\\\node_modules\\\\" // ], // 指示应使用哪个提供程序来检测代码以进行覆盖 coverageProvider: "v8", };
Demo
Tips: 一般单元测试建议写在utils文件夹下。
目录如下:
├── jest.config.js ├── package-lock.json ├── package.json ├── src │ └── utils │ └── sum.js └── liangzai-tests └── utils └── sum.test.js
/utils/sum.js
// sum.js function sum(a, b) { return a + b; } module.exports = sum;
/liangzai-utils/sum.test.js
const sum = require('../../utils/sum'); test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); });
然后执行
npm test
可以看到结果:
四、转译ts
Jest 本身不做代码转译工作:
安装ts
npm i -D typescript@4.6.3
初始化 TypeScript 的配置
npx tsc --init
执行后会看到tsconfig.json
文件:
{ "compilerOptions": { "types": ["node", "jest"], "target": "es2016", /* 为发出的 JavaScript 设置 JavaScript 语言版本并包含兼容的库声明 */ "module": "commonjs", /* 指定生成什么模块代码. */ "esModuleInterop": true, /* 发出额外的 JavaScript 以简化对导入 CommonJS 模块的支持。这将启用 `allowSyntheticDefaultImports` 以实现类型兼容性. */ "forceConsistentCasingInFileNames": true, /* 确保imports中的大小写正确 . */ "strict": true, /* 启用所有严格的类型检查选项。 */ "skipLibCheck": true /* 跳过类型检查所有 .d.ts 文件. */ } }
修改.js为.ts,代码增加类型
const sum = (a: number, b: number) => { return a + b; } export default sum;
安装Jest 类型声明包
npm i -D @types/jest@28.1.2
最后执行 npm run test
,测试通过。
小优化
路径使用简写,修改 tsconfig.json 配置:
{ "compilerOptions": { "paths": { "@/*": ["src/*"] } } }
jest.config.js修改moduleNameMapper
modulex.exports = { "moduleNameMapper": { "@/(.*)": "<rootDir>/src/$1" } }
五、其他知识点
setupFilesAfterEnv 和 setupFiles
简单来说:
setupFiles
是在 引入测试环境(比如下面的jsdom
)之后 执行的代码setupFilesAfterEnv
可以指定一个文件,在每执行一个测试文件前都会跑一遍里面的代码。
具体应用场景是:在 setupFiles
可以添加 测试环境 的补充,比如 Mock 全局变量 abcd
等。而在 setupFilesAfterEnv
可以引入和配置 Jest/Jasmine(Jest 内部使用了 Jasmine) 插件。
jsdom 测试环境
jest
提供了 testEnvironment
配置:
module.exports = { testEnvironment: "jsdom", }
jsdom: 这个库用 JS 实现了一套 Node.js 环境下的 Web 标准 API。
添加 jsdom 测试环境后,全局会自动拥有完整的浏览器标准 API,不需要Mock了。
引入react/vue
step1: 安装Webpack 依赖
step2: 安装相应的Loader
step3: 安装React/vue 以及业务
这里列举下webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'development', entry: { index: './src/index.tsx' }, module: { rules: [ // 解析 TypeScript { test: /\.(tsx?|jsx?)$/, use: 'ts-loader', exclude: /(node_modules|tests)/ }, // 解析 CSS { test: /\.css$/i, use: [ { loader: 'style-loader' }, { loader: 'css-loader' }, ] }, // 解析 Less { test: /\.less$/i, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: { mode: (resourcePath) => { if (/pure.css$/i.test(resourcePath)) { return "pure"; } if (/global.css$/i.test(resourcePath)) { return "global"; } return "local"; }, } } }, { loader: "less-loader" }, ], }, ], }, resolve: { extensions: ['.tsx', '.ts', '.js', '.less', 'css'], // 设置别名 alias: { utils: path.join(__dirname, 'src/utils/'), components: path.join(__dirname, 'src/components/'), apis: path.join(__dirname, 'src/apis/'), hooks: path.join(__dirname, 'src/hooks/'), store: path.join(__dirname, 'src/store/'), } }, devtool: 'inline-source-map', // 3000 端口打开网页 devServer: { static: './dist', port: 3000, hot: true, }, // 默认输出 output: { filename: 'index.js', path: path.resolve(__dirname, 'dist'), clean: true, }, // 指定模板 html plugins: [ new HtmlWebpackPlugin({ template: './public/index.html', }), ], };
package.json
添加启动命令
{ "scripts": { "start": "webpack serve", "test": "jest" } }
配置 tsconfig.json
{ "compilerOptions": { "jsx": "react", "esModuleInterop": true, "baseUrl": "./", "paths": { "utils/*": ["src/utils/*"], "components/*": ["src/components/*"], "apis/*": ["src/apis/*"], "hooks/*": ["src/hooks/*"], "store/*": ["src/store/*"] } } }