preface
In 2021, your front end project is still running naked, right?
Jest has become the standard for most front-end projects, every time it comes to Jest, Webpack, ESLint and other configurations, the brain is buzzing 🤯 in many configurations, sometimes a “rivet” configuration, can let the program or test running efficiency significantly reduced, “flowers in the” inevitably have a leaf stick body. As for why I am writing this article, I am working on a project where there is a Jest configuration problem, resulting in more than 60 test cases running 790s in –no-cache condition ☠️
So here are some common configurations of Jest: 😉
To the chase
module.exports = {
setupFiles: [
'react-app-polyfill/jsdom'.'<rootDir>/test/unit/jest.setup.js'.'core-js',].testMatch: [
'<rootDir>/src/**/__tests__/**/*.{spec,test}.{js,jsx,ts,tsx}'.'<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}',].transform: {
'^.+\.(js|jsx|ts|tsx)$': '<rootDir>/node_modules/babel-jest'.'^.+\.(css|less)$': '<rootDir>/config/jest/cssTransform.js'.'^ (? ! .*\.(js|jsx|ts|tsx|css|json)$)': '<rootDir>/config/jest/fileTransform.js',},transformIgnorePatterns: [
'[/\\]node_modules/(? ! (antd)/)[/\\].+\.(js|jsx|ts|tsx)$',].moduleNameMapper: {
'^react-native$': 'react-native-web'.'^.+\.module\.(css|sass|scss|less)$': 'identity-obj-proxy'.'\.svg$': 'identity-obj-proxy'.'test/(.*)': '<rootDir>/test/$1'.'^src/(.*)': '<rootDir>/src/$1',},moduleFileExtensions: [
'web.js'.'js'.'web.ts'.'ts'.'web.tsx'.'tsx'.'json'.'web.jsx'.'jsx'.'node',].// Other configurations have been omitted
};
Copy the code
Configuration optimization for Jest consists of the following two points:
- Less: Reduce unnecessary elements (such as images, styles, etc.);
- More accurate: Reduces the time to find a match in the file system;
Before taking a look at the following optimizations, take a look at the Jest configuration and see if there are any optimizations you can think of 🤔
setupFiles
As the old saying goes, “Thinking is the best way to prepare,” but is it really necessary to have “everything” when running a test?
SetupFiles is the “minister of the House” of Jest, ranking second! SetupFile: @testing-library/jest-dom: setupFile: @testing-library/jest-dom: setupFile: @testing-library/jest-dom And let’s go back to the configuration:
{
"setupFiles": [
"react-app-polyfill/jsdom"."<rootDir>/test/unit/jest.setup.js"."core-js"]}Copy the code
React-app-polyfill /jsdom
// True, source code only these three lines, the original "neihufu" also has "outsourcing project" 🤡
if (typeof window! = ='undefined') {
require ('whatwg-fetch');
}
Copy the code
Whatwg-fetch and core-js can simply be understood as polyfilling some of the new standards of the day, but we have Babel-Jest, why do you need them? Decisive “open drop”, save expenses 🙊
{
"setupFiles": ["<rootDir>/test/unit/jest.setup.js"]}Copy the code
moduleFileExtensions
The moduleFileExtensions are “extensions” for each “country” (module) in Jest to travel around. Bound for countries also have to have a order is not, or it will increase “accommodation expenses for drink horse”, if xu xiake, if isn’t he the abundance of his possessions, or likely will starve to death on the way 🐶 Zhang Qiantong xiyu court have route planning (default configuration), of course, he could improvise (custom configuration), in most cases the default configuration is practicable, It just might take a little more time on the road:
// Default configuration
["js"."jsx"."ts"."tsx"."json"."node"]
Copy the code
ModuleFileExtensions will look for the corresponding extension from left to right, but it might be better to tweak it slightly in a TypeScript + React project:
/ / adjusted
["ts"."tsx"."js"."jsx"."json"."node"]
Copy the code
This will reduce the number of times you have to search for extension and save money on gas.
moduleNameMapper
The source code for NPM dependencies is divided into ESM and CJS modules, while those like React are divided into CJS and UMD. Take ANTD as an example, its structure is as follows:
antd
|- lib/
|- es/
|- package.json
Copy the code
Json, you can specify the directory of ESM and CJS package files. Take the RC-select component package used in ANTD as an example:
{
"version": 12.1.5 ""."main": "./lib/index"."module": "./es/index"
}
Copy the code
To use the CJS package file, the tool queries the entry file in the file path corresponding to the main field. To use the ESM package file, the tool queries the entry file corresponding to the Module field.
In rC-util, however, the main and module fields are not specified, so they are used like this:
// rc-select/es/utils/legacyUtils.js
import toArray from 'rc-util/es/Children/toArray';
Copy the code
“What are you talking about? Why are you talking about so much?”
Since Jest currently supports the CJS specification and antD is used in the project, Jest cannot handle the RC-util dependency it uses and needs to manually convert it. This requires the introduction of a Jest configuration field called moduleNameMapper. The description document for this configuration field is as follows:
A map from regular expressions to module names that allow to stub out resources, like images or styles with a single module.
Stub is used to stub some resource files or module, can be matched to the content of the mapping to your specified content, even if “pointing a cat” is also possible! In front end unit testing, there is often a lot of content that is not needed, such as static resources, style files, etc. Then you can refer to these “deer” as “horse”.
We often put “deer 🦌” refers to identit-obJ-proxy this tool, although identit-OBJ-proxy last released 5 years ago, but it is very easy to use, and the source code is very simple (2 minutes you can not read the source code you follow the signal 📶 to hit me)!
module.exports = {
// ...
moduleNameMapper: {
'\.svg$': 'identity-obj-proxy'.'\.css$': 'identity-obj-proxy',}};Copy the code
It’s easy to refer antd’s es to lib:
module.exports = {
// ...
moduleNameMapper: {
'antd/es/(.*)': 'antd/lib/$1',}};Copy the code
This principle can be achieved with moduleNameMapper, but the transformIgnorePatterns and other related fields described below can also be used to process fewer resources or irrelevant resources.
transform
“Tianlong eight”, Ma Dayuan lady Kang Min will be a “borrow to kill” to play incisively and vividly, and in Jest transform also “by the hands of others to get rid of dissent”, as for kang Min’s intentions to talk again next time, this time will say why the transform “under this poisonous hand”. As mentioned above, Jest supports CJS. However, in current front-end projects, esM specifications such as import/export are generally used for modular development, so we need to “borrow other’s hands” to deal with such resources:
module.exports = {
// ...
transform: {
'^.+\.(js|jsx|ts|tsx)$': 'babel-jest',}};Copy the code
Transform can also convert ESM resources from ANTD to CJS, but why do you want to use them when you can civilize them?
transformIgnorePatterns
As you can tell by name, this configuration is something that “Kang Min” doesn’t care about, and the default value is [‘node_modules’], which is also pretty understandable. But let’s go back to the original configuration of the article:
module.exports = {
// ...
transformIgnorePatterns: [
'[/\\]node_modules/(? ! (antd)/)[/\\].+\.(js|jsx|ts|tsx)$',]};Copy the code
The original intention of the project was to use transform to process the antD resources introduced, but this resulted in traversing the entire node_modules file system during transform. Node_modules content was very large, so it took a lot of time to scan. Test run found “Qiao Feng found his father” 🌚
So the “one configuration problem” I mentioned at the beginning of this article is here. It’s much faster to delete transformIgnorePatterns and use moduleNameMapper instead.
The end of the
When issues of optimization, best practices, etc. are taken out of the context of a specific project, that’s “going rogue”! When I was in school, my teacher often said, “Look at it in detail.” After optimization, the test time was reduced from 790s to 40s, which is still an acceptable time ✌️
I wish you a happy National Day!