共计 3700 个字符,预计需要花费 10 分钟才能阅读完成。
Why
The more your tests resemble the way your software is used, the more confidence they can give you.
背景
该我的项目是用 umi 框架搭建,其本身内置了 jest 测试框架
- umi 内置的 jest 配置
{
collectCoverageFrom: ['index.{js,jsx,ts,tsx}',
hasSrc && 'src/**/*.{js,jsx,ts,tsx}',
isLerna && !args.package && 'packages/*/src/**/*.{js,jsx,ts,tsx}',
isLerna &&
args.package &&
`packages/${args.package}/src/**/*.{js,jsx,ts,tsx}`,
'!**/typings/**',
'!**/types/**',
'!**/fixtures/**',
'!**/examples/**',
'!**/*.d.ts',
].filter(Boolean),
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json'],
moduleNameMapper: {'\\.(css|less|sass|scss|stylus)$': require.resolve('identity-obj-proxy'),
},
setupFiles: [require.resolve('../../helpers/setupFiles/shim')],
setupFilesAfterEnv: [require.resolve('../../helpers/setupFiles/jasmine')],
testEnvironment: require.resolve('jest-environment-jsdom-fourteen'),
testMatch: [`${testMatchPrefix}**/?*.(${testMatchTypes.join('|')}).(j|t)s?(x)`,
],
testPathIgnorePatterns: ['/node_modules/', '/fixtures/'],
transform: {'^.+\\.(js|jsx|ts|tsx)$': require.resolve('../../helpers/transformers/javascript',),
'^.+\\.(css|less|sass|scss|stylus)$': require.resolve('../../helpers/transformers/css',),
'^(?!.*\\.(js|jsx|ts|tsx|css|less|sass|scss|stylus|json)$)': require.resolve('../../helpers/transformers/file',),
},
verbose: true,
transformIgnorePatterns: [// 加 [^/]*? 是为了兼容 tnpm 的目录构造
// 比方:_umi-test@1.5.5@umi-test
// `node_modules/(?!([^/]*?umi|[^/]*?umi-test)/)`,
],
// 用于设置 jest worker 启动的个数
...(process.env.MAX_WORKERS
? {maxWorkers: Number(process.env.MAX_WORKERS) }
: {}),
}
- 合并配置局部代码如下:
const config = mergeConfig(createDefaultConfig(cwd, args),
packageJestConfig,
userJestConfig
);
其源码在此:点击跳转
技术栈抉择
因为 umi 内置的 jest 配置是不能满足咱们我的项目的需要的,jest 是 JavaScript 的测试框架,如果须要对 dom 做单元测试,须要引入更多测试库
Jest
jest 是 umi 自带的,这个是根底,须要对其有根本的意识,其文档如下:
https://jestjs.io/zh-Hans/
ts-jest
ts-jest 是让咱们能用 Typescript 写测试代码的转换工具,也是 umi 内置配置,很少须要更改,如需更改可参考官网文档:
https://kulshekhar.github.io/ts-jest/docs/
React Testing Library
React Testing Library 是 测试 React 组件的解决方案,其蕴含了 React 官网的测试工具 react-dom/test-utils 的 API,也是 React 官网举荐的测试 DOM 库。
至于为什么不必老牌测试库 Enzyme , 能够看作者的 blog 这篇文章:
https://kentcdodds.com/blog/introducing-the-react-testing-library
平时写测试代码时更多接触的是 React Testing Library , 其文档如下:
https://testing-library.com/docs/react-testing-library/intro
除了 @testing-library/react 外,平时 @testing-library 旗下的其余库,例如 @testing-library/user-event 用来模仿交互的。@testing-library/react 是基于 @testing-library/dom 的,所以对 @testing-library/dom 有根本的理解,有助于深刻了解 @testing-library/react
Mock Service Worker
Mock Service Worker (MSW) 是用来 mock Axios 的申请后果的,执行 REST/GraphQL API
其文档如下:https://github.com/mswjs/msw
应用疏导
单元测试编写
这里不做过多介绍,能够参考官网 example 及其他领导文章,以及现有的单元测试代码,分享一下如下几篇
- 官网 demo:https://testing-library.com/docs/react-testing-library/example-intro
- 作者 blog—Common mistakes with React Testing Library — https://kentcdodds.com/blog/common-mistakes-with-react-testing-library
- 测试 react-toolkit — https://ogzhanolguncu.com/blog/testing-react-redux-toolkit-with-typescript
注:写单元测试可能加深你对代码的了解以及对本人代码的信任度
运行
单元测试环境曾经搭建好了,只须要执行对应的命名就能够了
"test": "umi test --no-cache", // 一次性执行单元测试
"test:watch": "umi test --watch", // 实时监听代码变动,执行
"test:clear": "umi test --clearCache",
"test:coverage": "umi test --coverage", // 执行,输入笼罩报告
配置
有些状况现有的配置不能满足本人的需要,能够依据状况更改配置
- jest.config.js
- ./src/setupTests.ts
覆盖率
应用了 Istanbul 代码笼罩工具,对于笼罩指标参考:
阮一峰这篇文章 — http://www.ruanyifeng.com/blog/2015/06/istanbul.html
目前我的项目确定如下的笼罩指标:
global: {
branches: 90,
functions: 90,
lines: 90,
statements: 90,
},
应用 yarn test:coverage
能够测试输入覆盖率报告
踩坑过程问题记录
记录一些问题:以防反复踩坑
- Jest test fails : TypeError: window.matchMedia is not a function
- Jest + react-testing-library: Warning update was not wrapped in act()
- Uncaught [Error: Invalid hook call. Hooks can only be called inside of the body of a function component — mock module 在 setupTests.ts 中引入
- The module factory of jest.mock() is not allowed to reference any out-of-scope variable — 用 require 在外面解决, 参考:https://github.com/facebook/jest/issues/2567
后续打算
- 抽离公共测试代码,使写单测更加丝滑
- 补充单元测试,满足覆盖率指标
- 端对端测试搞起来
- 接入到 CI/CD 中