neo-cmp-cli 1.12.7 → 1.12.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/README.md +204 -6
  2. package/dist/index2.js +1 -1
  3. package/dist/neo/env.js +1 -1
  4. package/dist/package.json.js +1 -1
  5. package/package.json +1 -1
  6. package/template/antd-custom-cmp-template/package.json +1 -1
  7. package/template/{neo-bi-cmps → asset-manage-template}/README.md +65 -10
  8. package/template/asset-manage-template/docs/README.md +244 -0
  9. package/template/asset-manage-template/neo.config.js +60 -0
  10. package/template/{neo-bi-cmps → asset-manage-template}/package.json +28 -16
  11. package/template/asset-manage-template/src/assets/img/chart.svg +1 -0
  12. package/template/asset-manage-template/src/components/README.md +3 -0
  13. package/template/asset-manage-template/src/components/assetManage__c/assetApi.ts +70 -0
  14. package/template/asset-manage-template/src/components/assetManage__c/cmps/AssetCreateModal.tsx +260 -0
  15. package/template/asset-manage-template/src/components/assetManage__c/cmps/AssetGrid.tsx +48 -0
  16. package/template/asset-manage-template/src/components/assetManage__c/cmps/AssetSidebar.tsx +74 -0
  17. package/template/asset-manage-template/src/components/assetManage__c/cmps/AssetToolbar.tsx +79 -0
  18. package/template/asset-manage-template/src/components/assetManage__c/cmps/assetDisplay.tsx +72 -0
  19. package/template/asset-manage-template/src/components/assetManage__c/constants.ts +28 -0
  20. package/template/asset-manage-template/src/components/assetManage__c/index.tsx +258 -0
  21. package/template/asset-manage-template/src/components/assetManage__c/model.ts +75 -0
  22. package/template/asset-manage-template/src/components/assetManage__c/style.scss +425 -0
  23. package/template/asset-manage-template/src/components/assetManage__c/types.ts +60 -0
  24. package/template/asset-manage-template/src/components/bidList__c/cmps/BidCard.tsx +47 -0
  25. package/template/asset-manage-template/src/components/bidList__c/constants.ts +6 -0
  26. package/template/asset-manage-template/src/components/bidList__c/formatUtils.ts +14 -0
  27. package/template/asset-manage-template/src/components/bidList__c/index.tsx +194 -0
  28. package/template/asset-manage-template/src/components/bidList__c/model.ts +57 -0
  29. package/template/asset-manage-template/src/components/bidList__c/style.scss +179 -0
  30. package/template/asset-manage-template/src/components/bidList__c/types.ts +10 -0
  31. package/template/asset-manage-template/src/components/bidPackage__c/cmps/BidPackageHeader.tsx +140 -0
  32. package/template/asset-manage-template/src/components/bidPackage__c/cmps/PackageItemTable.tsx +148 -0
  33. package/template/asset-manage-template/src/components/bidPackage__c/index.tsx +394 -0
  34. package/template/asset-manage-template/src/components/bidPackage__c/mainTableColumns.tsx +57 -0
  35. package/template/asset-manage-template/src/components/bidPackage__c/model.ts +86 -0
  36. package/template/asset-manage-template/src/components/bidPackage__c/style.scss +256 -0
  37. package/template/asset-manage-template/src/components/bidPackage__c/types.ts +35 -0
  38. package/template/asset-manage-template/src/components/bidPackage__c/utils.ts +19 -0
  39. package/template/{neo-bi-cmps → asset-manage-template}/src/utils/axiosFetcher.ts +0 -0
  40. package/template/{neo-bi-cmps → asset-manage-template}/src/utils/queryObjectData.ts +0 -0
  41. package/template/asset-manage-template/src/utils/url.ts +82 -0
  42. package/template/{neo-bi-cmps → asset-manage-template}/src/utils/xobjects.ts +0 -0
  43. package/template/{neo-bi-cmps → asset-manage-template}/tsconfig.json +1 -1
  44. package/template/echarts-custom-cmp-template/package.json +1 -1
  45. package/template/empty-custom-cmp-template/package.json +2 -2
  46. package/template/neo-custom-cmp-template/package.json +2 -2
  47. package/template/neo-custom-cmp-template/src/components/entityTable__c/index.tsx +62 -6
  48. package/template/neo-h5-cmps/neo.config.js +34 -76
  49. package/template/neo-h5-cmps/package.json +7 -3
  50. package/template/neo-h5-cmps/src/components/entityList__c/index.tsx +0 -4
  51. package/template/neo-h5-cmps/src/components/entityList__c/model.ts +10 -5
  52. package/template/neo-h5-cmps/src/components/entityTabs__c/index.tsx +29 -17
  53. package/template/neo-h5-cmps/src/components/entityTabs__c/model.ts +25 -5
  54. package/template/neo-h5-cmps/src/components/entityTabs__c/style.scss +11 -22
  55. package/template/neo-h5-cmps/src/components/openChatPageBtn__c/index.tsx +3 -1
  56. package/template/neo-h5-cmps/src/utils/xobjects.ts +8 -3
  57. package/template/neo-h5-cmps/tsconfig.json +1 -1
  58. package/template/neo-order-cmps/package.json +2 -2
  59. package/template/react-custom-cmp-template/package.json +1 -1
  60. package/template/react-ts-custom-cmp-template/package.json +1 -1
  61. package/template/vue2-custom-cmp-template/package.json +1 -1
  62. package/template/develop/BI /351/241/271/347/233/256/345/210/206/346/236/220/346/212/245/345/221/212.md" +0 -562
  63. package/template/develop/ChatPage /347/273/204/344/273/266/344/275/277/347/224/250/350/257/264/346/230/216/346/226/207/346/241/243.md" +0 -507
  64. package/template/develop/EntityGrid Web /347/273/204/344/273/266/347/232/204/350/257/246/347/273/206/345/210/206/346/236/220/346/226/207/346/241/243.md" +0 -868
  65. package/template/develop/EntityList H5 /347/273/204/344/273/266/347/232/204/350/257/246/347/273/206/345/210/206/346/236/220/346/226/207/346/241/243.md" +0 -1386
  66. package/template/develop/GlobalSearch/347/273/204/344/273/266/345/257/271/346/257/224/345/210/206/346/236/220.md +0 -866
  67. package/template/develop/Neo /344/270/255/345/217/257/347/224/250 amis /347/273/204/344/273/266.md" +0 -1490
  68. package/template/develop/cmpEventFunctions.ts +0 -257
  69. package/template/develop/cmpEvents.ts +0 -864
  70. package/template/develop/comTree/347/224/237/346/210/220/350/277/207/347/250/213/345/210/206/346/236/220.md +0 -469
  71. package/template/develop/commonModules.js +0 -55
  72. package/template/develop/components-table.md +0 -50
  73. package/template/develop/neo-custom-cmp-template/README.md +0 -48
  74. package/template/develop/neo-custom-cmp-template/docs/README.md +0 -13
  75. package/template/develop/neo-custom-cmp-template/neo.config.js +0 -121
  76. package/template/develop/neo-custom-cmp-template/package.json +0 -63
  77. package/template/develop/neo-custom-cmp-template/src/components/contactCardList/README.md +0 -65
  78. package/template/develop/neo-custom-cmp-template/src/components/contactCardList/index.tsx +0 -180
  79. package/template/develop/neo-custom-cmp-template/src/components/contactCardList/model.ts +0 -50
  80. package/template/develop/neo-custom-cmp-template/src/components/contactCardList/style.scss +0 -260
  81. package/template/develop/neo-custom-cmp-template/src/components/contactForm/README.md +0 -94
  82. package/template/develop/neo-custom-cmp-template/src/components/contactForm/index.tsx +0 -252
  83. package/template/develop/neo-custom-cmp-template/src/components/contactForm/model.ts +0 -56
  84. package/template/develop/neo-custom-cmp-template/src/components/contactForm/style.scss +0 -120
  85. package/template/develop/neo-custom-cmp-template/src/components/neoEntityGrid/README.md +0 -115
  86. package/template/develop/neo-custom-cmp-template/src/components/neoEntityGrid/index.tsx +0 -304
  87. package/template/develop/neo-custom-cmp-template/src/components/neoEntityGrid/model.ts +0 -87
  88. package/template/develop/neo-custom-cmp-template/src/components/neoEntityGrid/style.scss +0 -127
  89. package/template/develop/neo-custom-cmp-template/src/utils/axiosFetcher.ts +0 -29
  90. package/template/develop/neo-custom-cmp-template/src/utils/queryObjectData.ts +0 -39
  91. package/template/develop/neo-custom-cmp-template/src/utils/xobjects.ts +0 -203
  92. package/template/develop/neo-custom-cmp-template/tsconfig.json +0 -68
  93. package/template/develop/neo-ui-component-h5.md +0 -105
  94. package/template/develop/neo-ui-component-web-xregister.md +0 -31
  95. package/template/develop/neo-ui-component-web.md +0 -292
  96. package/template/develop/neoCmps.ts +0 -7508
  97. package/template/develop/neoGlobalSearchInput /347/273/204/344/273/266/347/232/204/350/257/246/347/273/206/345/210/206/346/236/220/346/226/207/346/241/243.md" +0 -497
  98. package/template/develop/pageSchema1.json +0 -744
  99. package/template/develop//344/272/213/344/273/266/345/212/250/344/275/234/344/277/235/345/255/230/346/265/201/347/250/213/345/210/206/346/236/220.md +0 -390
  100. package/template/develop//345/215/225/345/205/203/346/265/213/350/257/225/344/275/277/347/224/250/350/257/264/346/230/216.md +0 -1139
  101. package/template/neo-bi-cmps/.prettierrc.js +0 -12
  102. package/template/neo-bi-cmps/commitlint.config.js +0 -59
  103. package/template/neo-bi-cmps/neo.config.js +0 -124
  104. package/template/neo-bi-cmps/public/css/base.css +0 -283
  105. package/template/neo-bi-cmps/public/scripts/app/bluebird.js +0 -6679
  106. package/template/neo-bi-cmps/public/template.html +0 -13
  107. package/template/neo-bi-cmps/src/assets/css/common.scss +0 -127
  108. package/template/neo-bi-cmps/src/assets/css/mixin.scss +0 -47
  109. package/template/neo-bi-cmps/src/assets/img/NeoCRM.jpg +0 -0
  110. package/template/neo-bi-cmps/src/assets/img/custom-widget.svg +0 -1
  111. package/template/neo-bi-cmps/src/assets/img/favicon.png +0 -0
  112. package/template/neo-bi-cmps/src/assets/img/table.svg +0 -1
  113. package/template/neo-bi-cmps/src/components/targetNumber__c/README.md +0 -100
  114. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/configSchema.ts +0 -253
  115. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.scss +0 -76
  116. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.tsx +0 -148
  117. package/template/neo-bi-cmps/src/components/targetNumber__c/index.tsx +0 -440
  118. package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +0 -128
  119. package/template/neo-bi-cmps/src/components/targetNumber__c/style.scss +0 -173
  120. package/template/neo-h5-cmps/src/components/simpleTable__c/README.md +0 -90
  121. package/template/neo-h5-cmps/src/components/simpleTable__c/index.tsx +0 -277
  122. package/template/neo-h5-cmps/src/components/simpleTable__c/model.ts +0 -91
  123. package/template/neo-h5-cmps/src/components/simpleTable__c/style.scss +0 -116
  124. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/.prettierrc.js +0 -0
  125. /package/template/{neo-bi-cmps → asset-manage-template}/@types/neo-ui-common.d.ts +0 -0
  126. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/commitlint.config.js +0 -0
  127. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/public/css/base.css +0 -0
  128. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/public/scripts/app/bluebird.js +0 -0
  129. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/public/template.html +0 -0
  130. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/css/common.scss +0 -0
  131. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/css/mixin.scss +0 -0
  132. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/AIBtn.gif +0 -0
  133. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/img/NeoCRM.jpg +0 -0
  134. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/aiLogo.png +0 -0
  135. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/card-list.svg +0 -0
  136. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/contact-form.svg +0 -0
  137. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/custom-form.svg +0 -0
  138. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/img/custom-widget.svg +0 -0
  139. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/data-list.svg +0 -0
  140. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/detail.svg +0 -0
  141. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/img/favicon.png +0 -0
  142. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/map.svg +0 -0
  143. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/search.svg +0 -0
  144. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/img/table.svg +0 -0
@@ -1,1139 +0,0 @@
1
- # Jest 使用指南
2
-
3
- ## 目录
4
-
5
- - [什么是 Jest](#什么是-jest)
6
- - [快速开始](#快速开始)
7
- - [Jest 配置](#jest-配置)
8
- - [基础语法](#基础语法)
9
- - [测试文件组织](#测试文件组织)
10
- - [常用断言](#常用断言)
11
- - [Mock 使用](#mock-使用)
12
- - [异步测试](#异步测试)
13
- - [生命周期钩子](#生命周期钩子)
14
- - [测试覆盖率](#测试覆盖率)
15
- - [常见问题](#常见问题)
16
- - [最佳实践](#最佳实践)
17
-
18
- ## 什么是 Jest
19
-
20
- Jest 是 Facebook 开发的一个 JavaScript 测试框架,具有以下特点:
21
-
22
- - ✅ **零配置**:开箱即用,无需复杂配置
23
- - ✅ **快速**:并行运行测试,提高效率
24
- - ✅ **内置 Mock**:强大的 Mock 功能
25
- - ✅ **代码覆盖率**:自动生成覆盖率报告
26
- - ✅ **快照测试**:轻松测试 UI 组件
27
-
28
- ## 快速开始
29
-
30
- ### 安装
31
-
32
- ```bash
33
- npm install --save-dev jest
34
- # 或
35
- yarn add --dev jest
36
- ```
37
-
38
- ### 运行测试
39
-
40
- #### 如何启动 Jest
41
-
42
- Jest 可以通过多种方式启动:
43
-
44
- **1. 使用 npm/yarn 脚本(推荐)**
45
-
46
- 在 `package.json` 中配置测试脚本:
47
-
48
- ```json
49
- {
50
- "scripts": {
51
- "test": "jest",
52
- "test:watch": "jest --watch",
53
- "test:coverage": "jest --coverage"
54
- }
55
- }
56
- ```
57
-
58
- 然后运行:
59
- ```bash
60
- # 运行所有测试
61
- npm test
62
- # 或
63
- yarn test
64
-
65
- # 监听模式(文件变化时自动运行)
66
- npm test -- --watch
67
-
68
- # 生成覆盖率报告
69
- npm test -- --coverage
70
- ```
71
-
72
- **2. 直接使用 Jest 命令**
73
-
74
- 如果全局安装了 Jest:
75
- ```bash
76
- jest
77
- jest --watch
78
- jest --coverage
79
- ```
80
-
81
- **3. 使用 npx(无需全局安装)**
82
-
83
- ```bash
84
- npx jest
85
- npx jest --watch
86
- npx jest --coverage
87
- ```
88
-
89
- **4. 运行特定测试**
90
-
91
- ```bash
92
- # 运行特定文件
93
- npm test -- index.test.tsx
94
-
95
- # 运行匹配模式的文件
96
- npm test -- --testNamePattern="应该正确初始化"
97
-
98
- # 运行特定目录
99
- npm test -- src/components/Button
100
- ```
101
-
102
- **5. 其他常用选项**
103
-
104
- ```bash
105
- # 只运行失败的测试
106
- npm test -- --onlyFailures
107
-
108
- # 更新快照
109
- npm test -- --updateSnapshot
110
-
111
- # 详细输出
112
- npm test -- --verbose
113
- ```
114
-
115
- ## Jest 配置
116
-
117
- ### 配置文件位置
118
-
119
- Jest 支持多种配置方式,按优先级从高到低:
120
-
121
- 1. **`jest.config.js`** 或 **`jest.config.ts`**(推荐)
122
- - 放在项目根目录
123
- - 使用 CommonJS 或 ES6 模块语法
124
-
125
- 2. **`package.json`** 中的 `jest` 字段
126
- ```json
127
- {
128
- "jest": {
129
- "testEnvironment": "jsdom"
130
- }
131
- }
132
- ```
133
-
134
- 3. **命令行参数**
135
- ```bash
136
- jest --testEnvironment=jsdom
137
- ```
138
-
139
- ### 基础配置选项
140
-
141
- #### 1. 测试环境 (testEnvironment)
142
-
143
- 指定测试运行的环境:
144
-
145
- ```javascript
146
- module.exports = {
147
- // 'node' - Node.js 环境(默认)
148
- // 'jsdom' - 浏览器环境(用于测试 React 组件)
149
- testEnvironment: 'jsdom'
150
- }
151
- ```
152
-
153
- #### 2. 测试文件匹配 (testMatch)
154
-
155
- 指定哪些文件被视为测试文件:
156
-
157
- ```javascript
158
- module.exports = {
159
- // 默认值
160
- testMatch: [
161
- '**/__tests__/**/*.[jt]s?(x)',
162
- '**/?(*.)+(spec|test).[jt]s?(x)'
163
- ],
164
-
165
- // 自定义:只匹配 __test__ 目录下的文件
166
- testMatch: ['**/__test__/**/*.test.(ts|tsx)']
167
- }
168
- ```
169
-
170
- #### 3. 搜索根目录 (roots)
171
-
172
- 指定 Jest 搜索测试文件的根目录:
173
-
174
- ```javascript
175
- module.exports = {
176
- roots: ['<rootDir>/src', '<rootDir>/__test__']
177
- }
178
- ```
179
-
180
- #### 4. 模块路径映射 (moduleNameMapper)
181
-
182
- 配置路径别名,类似于 webpack 的 alias:
183
-
184
- ```javascript
185
- module.exports = {
186
- moduleNameMapper: {
187
- // 路径别名
188
- '^@components/(.*)$': '<rootDir>/src/components/$1',
189
- '^@utils/(.*)$': '<rootDir>/src/utils/$1',
190
-
191
- // Mock 静态资源
192
- '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
193
- '\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js'
194
- }
195
- }
196
- ```
197
-
198
- #### 5. 模块目录 (moduleDirectories)
199
-
200
- 指定模块解析的目录:
201
-
202
- ```javascript
203
- module.exports = {
204
- moduleDirectories: ['node_modules', 'src']
205
- }
206
- ```
207
-
208
- ### TypeScript 项目配置
209
-
210
- #### 使用 ts-jest preset(推荐)
211
-
212
- ```javascript
213
- module.exports = {
214
- preset: 'ts-jest',
215
- testEnvironment: 'jsdom'
216
- }
217
- ```
218
-
219
- #### 自定义 TypeScript 转换
220
-
221
- ```javascript
222
- module.exports = {
223
- transform: {
224
- '^.+\\.(ts|tsx)$': [
225
- 'ts-jest',
226
- {
227
- // ts-jest 配置选项
228
- diagnostics: false, // 禁用类型检查(加快速度)
229
- isolatedModules: true, // 启用隔离模块模式
230
- tsconfig: {
231
- // TypeScript 编译选项
232
- target: 'ES2019',
233
- module: 'commonjs',
234
- jsx: 'react',
235
- esModuleInterop: true,
236
- experimentalDecorators: true // 支持装饰器
237
- }
238
- }
239
- ]
240
- }
241
- }
242
- ```
243
-
244
- ### React 项目配置
245
-
246
- #### 完整配置示例
247
-
248
- ```javascript
249
- module.exports = {
250
- // 测试环境
251
- testEnvironment: 'jsdom',
252
-
253
- // 详细输出
254
- verbose: true,
255
-
256
- // 搜索根目录
257
- roots: ['<rootDir>/src'],
258
-
259
- // TypeScript 预设
260
- preset: 'ts-jest',
261
-
262
- // 转换配置
263
- transform: {
264
- '^.+\\.(ts|tsx)$': ['ts-jest', { diagnostics: false }],
265
- '^.+\\.(js|jsx)$': 'babel-jest'
266
- },
267
-
268
- // 忽略转换的模块
269
- transformIgnorePatterns: [
270
- 'node_modules/(?!(uuid|axios)/)' // 不忽略 uuid 和 axios
271
- ],
272
-
273
- // 模块路径映射
274
- moduleNameMapper: {
275
- '^@/(.*)$': '<rootDir>/src/$1',
276
- '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
277
- '\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js'
278
- },
279
-
280
- // 测试文件匹配
281
- testMatch: ['**/__test__/**/*.test.(ts|tsx)'],
282
-
283
- // 模块文件扩展名
284
- moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
285
-
286
- // 测试前执行的脚本
287
- setupFilesAfterEnv: ['<rootDir>/__test__/setupTests.ts']
288
- }
289
- ```
290
-
291
- ### 全局设置文件 (setupFilesAfterEnv)
292
-
293
- 在每个测试文件运行前执行的脚本,用于全局配置:
294
-
295
- ```javascript
296
- // jest.config.js
297
- module.exports = {
298
- setupFilesAfterEnv: ['<rootDir>/__test__/setupTests.ts']
299
- }
300
- ```
301
-
302
- ```typescript
303
- // __test__/setupTests.ts
304
- import '@testing-library/jest-dom'
305
-
306
- // Mock ResizeObserver
307
- global.ResizeObserver = jest.fn().mockImplementation(() => ({
308
- observe: jest.fn(),
309
- unobserve: jest.fn(),
310
- disconnect: jest.fn()
311
- }))
312
-
313
- // Mock window.matchMedia
314
- Object.defineProperty(window, 'matchMedia', {
315
- writable: true,
316
- value: jest.fn().mockImplementation(query => ({
317
- matches: false,
318
- media: query,
319
- onchange: null,
320
- addListener: jest.fn(),
321
- removeListener: jest.fn(),
322
- addEventListener: jest.fn(),
323
- removeEventListener: jest.fn(),
324
- dispatchEvent: jest.fn()
325
- }))
326
- })
327
- ```
328
-
329
- ### 覆盖率配置
330
-
331
- #### 收集覆盖率
332
-
333
- ```javascript
334
- module.exports = {
335
- // 收集覆盖率的文件范围
336
- collectCoverageFrom: [
337
- 'src/**/*.{ts,tsx}',
338
- '!src/**/*.d.ts', // 排除类型定义文件
339
- '!src/**/*.test.{ts,tsx}', // 排除测试文件
340
- '!src/**/*.stories.{ts,tsx}', // 排除 Storybook 文件
341
- '!src/index.tsx' // 排除入口文件
342
- ],
343
-
344
- // 覆盖率阈值
345
- coverageThreshold: {
346
- global: {
347
- branches: 75, // 分支覆盖率至少 75%
348
- functions: 85, // 函数覆盖率至少 85%
349
- lines: 85, // 行覆盖率至少 85%
350
- statements: 85 // 语句覆盖率至少 85%
351
- }
352
- },
353
-
354
- // 覆盖率报告格式
355
- coverageReporters: ['text', 'lcov', 'html']
356
- }
357
- ```
358
-
359
- ### 其他常用配置
360
-
361
- #### 1. 全局变量 (globals)
362
-
363
- ```javascript
364
- module.exports = {
365
- globals: {
366
- __DEV__: true,
367
- __TEST__: true
368
- }
369
- }
370
- ```
371
-
372
- #### 2. 测试超时时间 (testTimeout)
373
-
374
- ```javascript
375
- module.exports = {
376
- testTimeout: 10000 // 10 秒(默认 5 秒)
377
- }
378
- ```
379
-
380
- #### 3. 最大并发数 (maxWorkers)
381
-
382
- ```javascript
383
- module.exports = {
384
- maxWorkers: '50%' // 使用 50% 的 CPU 核心
385
- // 或指定具体数量
386
- // maxWorkers: 4
387
- }
388
- ```
389
-
390
- #### 4. 清除 Mock (clearMocks)
391
-
392
- ```javascript
393
- module.exports = {
394
- clearMocks: true, // 每个测试前自动清除 Mock
395
- resetMocks: true, // 每个测试前自动重置 Mock
396
- restoreMocks: true // 每个测试前自动恢复原始实现
397
- }
398
- ```
399
-
400
- #### 5. 快照序列化器 (snapshotSerializers)
401
-
402
- ```javascript
403
- module.exports = {
404
- snapshotSerializers: ['enzyme-to-json/serializer']
405
- }
406
- ```
407
-
408
- ### 实际项目配置示例
409
-
410
- 基于本项目的实际配置:
411
-
412
- ```javascript
413
- // jest.config.js
414
- module.exports = {
415
- // 测试环境:浏览器环境(用于测试 React 组件)
416
- testEnvironment: 'jsdom',
417
-
418
- // 详细输出模式
419
- verbose: true,
420
-
421
- // 搜索根目录
422
- roots: ['<rootDir>/src', '<rootDir>/__test__'],
423
-
424
- // TypeScript 预设
425
- preset: 'ts-jest',
426
-
427
- // 转换配置
428
- transform: {
429
- '^.+\\.(ts|tsx)$': [
430
- 'ts-jest',
431
- {
432
- diagnostics: false,
433
- isolatedModules: true,
434
- tsconfig: {
435
- target: 'ES2019',
436
- module: 'commonjs',
437
- jsx: 'react',
438
- experimentalDecorators: true,
439
- emitDecoratorMetadata: true
440
- }
441
- }
442
- ],
443
- '^.+\\.(js|jsx)$': 'babel-jest'
444
- },
445
-
446
- // 忽略转换的模块(某些 ES6 模块需要转换)
447
- transformIgnorePatterns: ['node_modules/(?!(uuid|axios)/)'],
448
-
449
- // 模块目录
450
- moduleDirectories: ['node_modules', 'src'],
451
-
452
- // 模块路径映射
453
- moduleNameMapper: {
454
- // 包别名
455
- '^neo-ui-common$': '<rootDir>/../neo-ui-common/src',
456
- '^neo-ui-common/(.*)$': '<rootDir>/../neo-ui-common/src/$1',
457
-
458
- // 路径别名
459
- '^@components/(.*)$': '<rootDir>/src/components/$1',
460
- '^@utils/(.*)$': '<rootDir>/src/utils/$1',
461
-
462
- // Mock 静态资源
463
- '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
464
- '\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js',
465
-
466
- // Mock 特定模块
467
- '^uuid$': '<rootDir>/src/__mocks__/uuid.js'
468
- },
469
-
470
- // 测试前执行的脚本
471
- setupFilesAfterEnv: ['<rootDir>/__test__/setupTests.ts'],
472
-
473
- // 测试文件匹配规则
474
- testMatch: ['**/__test__/**/*.test.(ts|tsx)'],
475
-
476
- // 模块文件扩展名
477
- moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
478
-
479
- // 覆盖率配置
480
- collectCoverageFrom: [
481
- 'src/**/*.{ts,tsx}',
482
- '!src/**/*.d.ts',
483
- '!src/**/*.test.{ts,tsx}',
484
- '!src/**/*.stories.{ts,tsx}',
485
- '!src/index.tsx'
486
- ],
487
-
488
- // 覆盖率阈值
489
- coverageThreshold: {
490
- global: {
491
- branches: 75,
492
- functions: 85,
493
- lines: 85,
494
- statements: 85
495
- }
496
- }
497
- }
498
- ```
499
-
500
- ### 配置验证
501
-
502
- 运行以下命令验证配置是否正确:
503
-
504
- ```bash
505
- # 显示 Jest 配置信息
506
- npx jest --showConfig
507
-
508
- # 列出所有匹配的测试文件(不运行)
509
- npx jest --listTests
510
- ```
511
-
512
- ### 配置最佳实践
513
-
514
- 1. **使用 `jest.config.js`**:将配置放在独立的配置文件中,便于管理
515
- 2. **合理设置 `testMatch`**:明确指定测试文件的匹配规则
516
- 3. **配置路径别名**:使用 `moduleNameMapper` 简化导入路径
517
- 4. **Mock 静态资源**:避免在测试中处理图片、样式等静态资源
518
- 5. **设置覆盖率阈值**:确保代码质量
519
- 6. **使用 `setupFilesAfterEnv`**:统一全局测试环境配置
520
-
521
- ## 基础语法
522
-
523
- ### 最简单的测试
524
-
525
- ```javascript
526
- // math.test.js
527
- function add(a, b) {
528
- return a + b
529
- }
530
-
531
- test('1 + 2 应该等于 3', () => {
532
- expect(add(1, 2)).toBe(3)
533
- })
534
- ```
535
-
536
- ### 使用 describe 分组
537
-
538
- ```javascript
539
- describe('计算器功能', () => {
540
- test('加法测试', () => {
541
- expect(add(1, 2)).toBe(3)
542
- })
543
-
544
- test('减法测试', () => {
545
- expect(subtract(5, 2)).toBe(3)
546
- })
547
- })
548
- ```
549
-
550
- ### test 和 it 的区别
551
-
552
- `test` 和 `it` 是完全一样的,只是写法不同:
553
-
554
- ```javascript
555
- // 两种写法等价
556
- test('测试名称', () => {})
557
- it('测试名称', () => {})
558
- ```
559
-
560
- ## 测试文件组织
561
-
562
- ### Jest 如何找到单元测试文件?
563
-
564
- Jest 通过以下方式自动发现和运行测试文件:
565
-
566
- **1. 默认搜索规则**
567
-
568
- Jest 会按照以下规则查找测试文件:
569
- - 文件名包含 `.test.` 或 `.spec.` 的文件
570
- - 文件扩展名:`.js`、`.jsx`、`.ts`、`.tsx`
571
- - 默认搜索目录:项目根目录下的所有文件
572
-
573
- **2. 配置文件中的 `testMatch`**
574
-
575
- 在 `jest.config.js` 中可以自定义测试文件匹配规则:
576
-
577
- ```javascript
578
- module.exports = {
579
- // 自定义测试文件匹配模式
580
- testMatch: [
581
- '**/__test__/**/*.test.(ts|tsx)', // __test__ 目录下的 .test.ts 或 .test.tsx 文件
582
- '**/?(*.)+(spec|test).(ts|tsx)' // 任何位置的 .spec.ts 或 .test.ts 文件
583
- ]
584
- }
585
- ```
586
-
587
- **3. 配置文件中的 `roots`**
588
-
589
- 指定 Jest 搜索的根目录:
590
-
591
- ```javascript
592
- module.exports = {
593
- roots: ['<rootDir>/src', '<rootDir>/__test__']
594
- // Jest 只会在 src 和 __test__ 目录下搜索测试文件
595
- }
596
- ```
597
-
598
- **4. 搜索优先级**
599
-
600
- Jest 会按照以下顺序查找:
601
- 1. 首先检查 `testMatch` 配置
602
- 2. 如果没有配置,使用默认规则:`**/__tests__/**/*.[jt]s?(x)` 和 `**/?(*.)+(spec|test).[jt]s?(x)`
603
- 3. 在 `roots` 指定的目录中搜索(如果配置了的话)
604
-
605
- **5. 实际示例**
606
-
607
- 假设项目结构如下:
608
- ```
609
- project/
610
- ├── src/
611
- │ ├── components/
612
- │ │ ├── Button/
613
- │ │ │ ├── index.tsx
614
- │ │ │ └── __test__/
615
- │ │ │ └── index.test.tsx ✅ 会被找到
616
- │ │ └── Input.tsx
617
- │ └── utils.test.tsx ✅ 会被找到
618
- └── __test__/
619
- └── integration.test.tsx ✅ 会被找到
620
- ```
621
-
622
- 如果配置了 `testMatch: ['**/__test__/**/*.test.(ts|tsx)']`,则只有 `__test__` 目录下的测试文件会被找到。
623
-
624
- ### 文件命名
625
-
626
- - 测试文件以 `.test.js`、`.test.ts`、`.test.tsx` 结尾
627
- - 或者以 `.spec.js`、`.spec.ts`、`.spec.tsx` 结尾
628
-
629
- ### 目录结构
630
-
631
- 推荐的项目结构:
632
-
633
- ```
634
- src/
635
- ├── components/
636
- │ ├── Button/
637
- │ │ ├── index.tsx
638
- │ │ └── __test__/
639
- │ │ └── index.test.tsx
640
- │ └── Input/
641
- │ ├── index.tsx
642
- │ └── index.test.tsx
643
- ```
644
-
645
- **注意:**
646
- - 测试文件可以放在 `__test__` 目录下(推荐,便于管理)
647
- - 也可以放在与被测试文件相同的目录下(如 `Button.test.tsx` 与 `Button.tsx` 同级)
648
- - 根据项目配置的 `testMatch` 规则选择合适的命名和位置
649
-
650
- ## 常用断言
651
-
652
- ### 基础断言
653
-
654
- ```javascript
655
- // 相等性判断
656
- expect(1 + 1).toBe(2) // 严格相等 ===
657
- expect({a: 1}).toEqual({a: 1}) // 对象值相等
658
- expect({a: 1}).toStrictEqual({a: 1}) // 严格相等(推荐)
659
-
660
- // 真值判断
661
- expect(true).toBeTruthy()
662
- expect(false).toBeFalsy()
663
- expect(null).toBeNull()
664
- expect(undefined).toBeUndefined()
665
-
666
- // 数字比较
667
- expect(2).toBeGreaterThan(1)
668
- expect(1).toBeLessThan(2)
669
- expect(0.1 + 0.2).toBeCloseTo(0.3) // 浮点数比较
670
-
671
- // 字符串匹配
672
- expect('hello').toMatch(/llo/)
673
- expect('hello').toContain('ell')
674
-
675
- // 数组/对象包含
676
- expect([1, 2, 3]).toContain(2)
677
- expect({a: 1, b: 2}).toHaveProperty('a')
678
- ```
679
-
680
- ### 函数调用断言
681
-
682
- ```javascript
683
- const mockFn = jest.fn()
684
-
685
- mockFn('参数1', '参数2')
686
-
687
- // 检查是否被调用
688
- expect(mockFn).toHaveBeenCalled()
689
-
690
- // 检查调用次数
691
- expect(mockFn).toHaveBeenCalledTimes(1)
692
-
693
- // 检查调用参数
694
- expect(mockFn).toHaveBeenCalledWith('参数1', '参数2')
695
-
696
- // 检查最后一次调用
697
- expect(mockFn).toHaveBeenLastCalledWith('参数1', '参数2')
698
-
699
- // 检查返回值
700
- expect(mockFn).toHaveReturned()
701
- expect(mockFn).toHaveReturnedWith('返回值')
702
- ```
703
-
704
- ### 异常断言
705
-
706
- ```javascript
707
- // 应该抛出异常
708
- expect(() => {
709
- throw new Error('错误')
710
- }).toThrow()
711
-
712
- // 应该抛出特定错误
713
- expect(() => {
714
- throw new Error('错误')
715
- }).toThrow('错误')
716
-
717
- // 不应该抛出异常
718
- expect(() => {
719
- normalFunction()
720
- }).not.toThrow()
721
- ```
722
-
723
- ## Mock 使用
724
-
725
- ### 什么是 Mock?
726
-
727
- **Mock(模拟)** 是测试中的一个重要概念,用于创建虚假的依赖对象或函数,以便在测试中:
728
-
729
- 1. **隔离测试单元**:避免测试代码受到外部依赖(如 API、数据库、文件系统等)的影响
730
- 2. **控制测试环境**:模拟各种场景(成功、失败、边界情况)而不需要真实的外部服务
731
- 3. **提高测试速度**:避免真实的网络请求、数据库操作等耗时操作
732
- 4. **增强测试稳定性**:不依赖外部服务的可用性和状态
733
-
734
- **常见使用场景:**
735
- - Mock API 请求:避免在测试中发送真实的 HTTP 请求
736
- - Mock 模块依赖:替换难以测试的第三方模块
737
- - Mock 浏览器 API:模拟 `window`、`document`、`localStorage` 等浏览器对象
738
- - Mock 函数调用:验证函数是否被正确调用,以及调用参数和次数
739
-
740
- **示例说明:**
741
-
742
- 假设你要测试一个函数,它需要调用 API 获取用户数据:
743
-
744
- ```javascript
745
- // 不使用 Mock:需要真实的 API 服务,测试慢且不稳定
746
- async function getUserData() {
747
- const response = await fetch('/api/user') // 真实请求
748
- return response.json()
749
- }
750
-
751
- // 使用 Mock:快速、稳定、可控
752
- jest.mock('fetch', () => jest.fn())
753
- const mockFetch = require('fetch')
754
- mockFetch.mockResolvedValue({ data: { name: '测试用户' } })
755
- ```
756
-
757
- ### 基础 Mock
758
-
759
- ```javascript
760
- // 创建一个 Mock 函数
761
- const mockFn = jest.fn()
762
-
763
- // 设置返回值
764
- mockFn.mockReturnValue(42)
765
- expect(mockFn()).toBe(42)
766
-
767
- // 设置一次返回值
768
- mockFn.mockReturnValueOnce(1)
769
- mockFn.mockReturnValueOnce(2)
770
- expect(mockFn()).toBe(1)
771
- expect(mockFn()).toBe(2)
772
-
773
- // 模拟实现
774
- mockFn.mockImplementation((x) => x * 2)
775
- expect(mockFn(5)).toBe(10)
776
- ```
777
-
778
- ### Mock 模块
779
-
780
- ```javascript
781
- // 完全 Mock 一个模块
782
- jest.mock('./api', () => ({
783
- fetchData: jest.fn(() => Promise.resolve({data: 'test'}))
784
- }))
785
-
786
- // 部分 Mock(保留部分原功能)
787
- jest.mock('./utils', () => ({
788
- ...jest.requireActual('./utils'),
789
- specificFunction: jest.fn()
790
- }))
791
- ```
792
-
793
- ### Mock 外部依赖
794
-
795
- ```javascript
796
- // Mock axios
797
- jest.mock('axios', () => ({
798
- get: jest.fn(() => Promise.resolve({data: {}})),
799
- post: jest.fn(() => Promise.resolve({data: {}}))
800
- }))
801
-
802
- // Mock React 组件
803
- jest.mock('../Button', () => {
804
- return function MockButton({children}) {
805
- return <div data-testid="mock-button">{children}</div>
806
- }
807
- })
808
- ```
809
-
810
- ### 清理 Mock
811
-
812
- ```javascript
813
- beforeEach(() => {
814
- // 清理所有 Mock 的调用记录
815
- jest.clearAllMocks()
816
-
817
- // 重置 Mock 实现
818
- jest.resetAllMocks()
819
-
820
- // 恢复原始实现
821
- jest.restoreAllMocks()
822
- })
823
- ```
824
-
825
- ## 异步测试
826
-
827
- ### Promise 测试
828
-
829
- ```javascript
830
- // 使用 async/await(推荐)
831
- test('异步获取数据', async () => {
832
- const data = await fetchData()
833
- expect(data).toEqual({id: 1})
834
- })
835
-
836
- // 使用 .then()
837
- test('异步获取数据', () => {
838
- return fetchData().then(data => {
839
- expect(data).toEqual({id: 1})
840
- })
841
- })
842
- ```
843
-
844
- ### 错误处理测试
845
-
846
- ```javascript
847
- test('处理异步错误', async () => {
848
- await expect(fetchData()).rejects.toThrow('网络错误')
849
-
850
- // 或使用 try/catch
851
- try {
852
- await fetchData()
853
- } catch (error) {
854
- expect(error.message).toBe('网络错误')
855
- }
856
- })
857
- ```
858
-
859
- ### 等待 DOM 更新
860
-
861
- ```javascript
862
- import { waitFor } from '@testing-library/react'
863
-
864
- test('等待元素出现', async () => {
865
- render(<Component />)
866
-
867
- await waitFor(() => {
868
- expect(screen.getByText('加载完成')).toBeInTheDocument()
869
- })
870
- })
871
- ```
872
-
873
- ## 生命周期钩子
874
-
875
- ### 全局钩子
876
-
877
- ```javascript
878
- // 所有测试前执行一次
879
- beforeAll(() => {
880
- // 初始化数据库连接等
881
- })
882
-
883
- // 所有测试后执行一次
884
- afterAll(() => {
885
- // 清理资源
886
- })
887
- ```
888
-
889
- ### 每个测试的钩子
890
-
891
- ```javascript
892
- describe('测试套件', () => {
893
- // 每个测试前执行
894
- beforeEach(() => {
895
- // 重置状态
896
- jest.clearAllMocks()
897
- })
898
-
899
- // 每个测试后执行
900
- afterEach(() => {
901
- // 清理 DOM
902
- cleanup()
903
- })
904
-
905
- test('测试1', () => {
906
- // 测试代码
907
- })
908
-
909
- test('测试2', () => {
910
- // 测试代码
911
- })
912
- })
913
- ```
914
-
915
- ## 测试覆盖率
916
-
917
- ### 查看覆盖率
918
-
919
- ```bash
920
- npm test -- --coverage
921
- ```
922
-
923
- ### 覆盖率指标
924
-
925
- - **语句覆盖率**:代码中有多少语句被执行了
926
- - **分支覆盖率**:有多少 if/else 分支被执行了
927
- - **函数覆盖率**:有多少函数被调用了
928
- - **行覆盖率**:有多少行代码被执行了
929
-
930
- ### 覆盖率配置
931
-
932
- 在 `package.json` 中配置:
933
-
934
- ```json
935
- {
936
- "jest": {
937
- "collectCoverageFrom": [
938
- "src/**/*.{js,jsx,ts,tsx}",
939
- "!src/**/*.d.ts",
940
- "!src/**/__test__/**"
941
- ],
942
- "coverageThreshold": {
943
- "global": {
944
- "branches": 80,
945
- "functions": 80,
946
- "lines": 80,
947
- "statements": 80
948
- }
949
- }
950
- }
951
- }
952
- ```
953
-
954
- ## 常见问题
955
-
956
- ### 1. 如何测试 React 组件?
957
-
958
- ```javascript
959
- import { render, screen } from '@testing-library/react'
960
- import Component from './Component'
961
-
962
- test('渲染组件', () => {
963
- render(<Component name="测试" />)
964
- expect(screen.getByText('测试')).toBeInTheDocument()
965
- })
966
- ```
967
-
968
- ### 2. 如何测试用户交互?
969
-
970
- ```javascript
971
- import { fireEvent } from '@testing-library/react'
972
-
973
- test('点击按钮', () => {
974
- render(<Button onClick={handleClick}>点击</Button>)
975
- fireEvent.click(screen.getByText('点击'))
976
- expect(handleClick).toHaveBeenCalled()
977
- })
978
- ```
979
-
980
- ### 3. 如何处理定时器?
981
-
982
- ```javascript
983
- // 使用 Jest 的定时器 Mock
984
- jest.useFakeTimers()
985
-
986
- test('定时器测试', () => {
987
- const callback = jest.fn()
988
- setTimeout(callback, 1000)
989
-
990
- jest.advanceTimersByTime(1000)
991
- expect(callback).toHaveBeenCalled()
992
-
993
- jest.useRealTimers() // 恢复真实定时器
994
- })
995
- ```
996
-
997
- ### 4. 如何跳过测试?
998
-
999
- ```javascript
1000
- // 跳过单个测试
1001
- test.skip('这个测试被跳过', () => {})
1002
-
1003
- // 跳过整个测试套件
1004
- describe.skip('这个套件被跳过', () => {})
1005
-
1006
- // 条件跳过
1007
- test('条件测试', () => {
1008
- if (process.env.NODE_ENV === 'production') {
1009
- return // 跳过
1010
- }
1011
- // 测试代码
1012
- })
1013
- ```
1014
-
1015
- ### 5. 如何只运行特定测试?
1016
-
1017
- ```javascript
1018
- // 使用 test.only 或 it.only
1019
- test.only('只运行这个测试', () => {})
1020
-
1021
- // 或使用 describe.only
1022
- describe.only('只运行这个套件', () => {})
1023
- ```
1024
-
1025
- ## 最佳实践
1026
-
1027
- ### 1. 测试命名要清晰
1028
-
1029
- ```javascript
1030
- // ✅ 好的命名
1031
- test('当用户点击删除按钮时应该显示确认对话框', () => {})
1032
-
1033
- // ❌ 不好的命名
1034
- test('测试删除', () => {})
1035
- ```
1036
-
1037
- ### 2. 一个测试只测试一件事
1038
-
1039
- ```javascript
1040
- // ✅ 好的做法
1041
- test('应该正确计算总和', () => {
1042
- expect(sum([1, 2, 3])).toBe(6)
1043
- })
1044
-
1045
- test('应该处理空数组', () => {
1046
- expect(sum([])).toBe(0)
1047
- })
1048
-
1049
- // ❌ 不好的做法
1050
- test('测试 sum 函数', () => {
1051
- expect(sum([1, 2, 3])).toBe(6)
1052
- expect(sum([])).toBe(0)
1053
- expect(sum([-1, 1])).toBe(0)
1054
- })
1055
- ```
1056
-
1057
- ### 3. 使用描述性的断言消息
1058
-
1059
- ```javascript
1060
- // ✅ 好的做法
1061
- expect(result).toBe(expected, '计算结果应该等于预期值')
1062
-
1063
- // ❌ 不好的做法
1064
- expect(result).toBe(expected)
1065
- ```
1066
-
1067
- ### 4. 保持测试独立
1068
-
1069
- ```javascript
1070
- // ✅ 好的做法:每个测试独立
1071
- beforeEach(() => {
1072
- jest.clearAllMocks()
1073
- // 重置状态
1074
- })
1075
-
1076
- // ❌ 不好的做法:测试之间相互依赖
1077
- let sharedState = {}
1078
- ```
1079
-
1080
- ### 5. 合理使用 Mock
1081
-
1082
- ```javascript
1083
- // ✅ 好的做法:只 Mock 外部依赖
1084
- jest.mock('axios')
1085
- jest.mock('../api')
1086
-
1087
- // ❌ 不好的做法:过度 Mock
1088
- jest.mock('./utils') // 如果 utils 是内部工具,不应该 Mock
1089
- ```
1090
-
1091
- ### 6. 测试边界情况
1092
-
1093
- ```javascript
1094
- describe('边界情况', () => {
1095
- test('应该处理空值', () => {
1096
- expect(process(null)).toBe(null)
1097
- })
1098
-
1099
- test('应该处理空数组', () => {
1100
- expect(process([])).toEqual([])
1101
- })
1102
-
1103
- test('应该处理超大数字', () => {
1104
- expect(process(Number.MAX_SAFE_INTEGER)).toBeDefined()
1105
- })
1106
- })
1107
- ```
1108
-
1109
- ### 7. 使用测试工具函数
1110
-
1111
- ```javascript
1112
- // 创建测试数据的工厂函数
1113
- const createUser = (overrides = {}) => ({
1114
- id: 1,
1115
- name: '测试用户',
1116
- email: 'test@example.com',
1117
- ...overrides
1118
- })
1119
-
1120
- test('测试用户创建', () => {
1121
- const user = createUser({name: '新用户'})
1122
- expect(user.name).toBe('新用户')
1123
- })
1124
- ```
1125
-
1126
- ## 总结
1127
-
1128
- Jest 是一个功能强大且易于使用的测试框架。记住以下要点:
1129
-
1130
- 1. **写测试要简单**:一个测试只测试一件事
1131
- 2. **命名要清晰**:测试名称要能说明测试的目的
1132
- 3. **保持独立**:测试之间不应该相互依赖
1133
- 4. **合理 Mock**:只 Mock 外部依赖,不要过度 Mock
1134
- 5. **测试边界**:不仅要测试正常情况,也要测试边界和错误情况
1135
-
1136
- 更多详细信息,请参考:
1137
- - [Jest 官方文档](https://jestjs.io/docs/getting-started)
1138
- - [React Testing Library](https://testing-library.com/react)
1139
- - [项目测试规范](.cursor/rules/component-jest.mdc)