ai-engineering-init 1.1.0

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 (313) hide show
  1. package/.claude/agents/code-reviewer.md +139 -0
  2. package/.claude/agents/project-manager.md +159 -0
  3. package/.claude/audio/completed.wav +0 -0
  4. package/.claude/commands/add-todo.md +255 -0
  5. package/.claude/commands/check.md +210 -0
  6. package/.claude/commands/crud.md +454 -0
  7. package/.claude/commands/dev.md +503 -0
  8. package/.claude/commands/init-docs.md +681 -0
  9. package/.claude/commands/next.md +251 -0
  10. package/.claude/commands/progress.md +242 -0
  11. package/.claude/commands/start.md +199 -0
  12. package/.claude/commands/sync.md +307 -0
  13. package/.claude/commands/update-status.md +428 -0
  14. package/.claude/docs/Mixin/344/275/277/347/224/250/346/214/207/345/215/227.md +299 -0
  15. package/.claude/docs/README.md +167 -0
  16. package/.claude/docs//345/211/215/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +599 -0
  17. package/.claude/docs//345/220/216/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +726 -0
  18. package/.claude/docs//345/267/245/344/275/234/346/265/201/345/274/200/345/217/221/346/214/207/345/215/227.md +714 -0
  19. package/.claude/docs//345/267/245/345/205/267/347/261/273/344/275/277/347/224/250/346/214/207/345/215/227.md +463 -0
  20. package/.claude/docs//346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/350/247/204/350/214/203.md +390 -0
  21. package/.claude/docs//346/226/260/345/212/237/350/203/275/345/274/200/345/217/221/346/265/201/347/250/213/350/247/204/350/214/203.md +688 -0
  22. package/.claude/docs//346/226/260/351/241/271/347/233/256/345/274/200/345/217/221/346/265/201/347/250/213.md +365 -0
  23. package/.claude/docs//346/241/206/346/236/266/350/257/264/346/230/216.md +393 -0
  24. package/.claude/docs//350/267/257/347/224/261/351/205/215/347/275/256/346/214/207/345/215/227.md +246 -0
  25. package/.claude/framework-config.json +73 -0
  26. package/.claude/hooks/pre-tool-use.js +117 -0
  27. package/.claude/hooks/skill-forced-eval.js +167 -0
  28. package/.claude/hooks/stop.js +58 -0
  29. package/.claude/settings.json +41 -0
  30. package/.claude/skills/add-skill/SKILL.md +352 -0
  31. package/.claude/skills/api-development/SKILL.md +560 -0
  32. package/.claude/skills/architecture-design/SKILL.md +756 -0
  33. package/.claude/skills/backend-annotations/SKILL.md +674 -0
  34. package/.claude/skills/banana-image/CHANGELOG.md +37 -0
  35. package/.claude/skills/banana-image/README.md +146 -0
  36. package/.claude/skills/banana-image/SKILL.md +164 -0
  37. package/.claude/skills/banana-image/assets/logo.png +0 -0
  38. package/.claude/skills/banana-image/references/advanced-usage.md +189 -0
  39. package/.claude/skills/banana-image/scripts/apply_template.py +125 -0
  40. package/.claude/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  41. package/.claude/skills/banana-image/scripts/batch_prep.py +82 -0
  42. package/.claude/skills/banana-image/scripts/package-lock.json +1437 -0
  43. package/.claude/skills/banana-image/scripts/package.json +18 -0
  44. package/.claude/skills/banana-image/scripts/requirements.txt +10 -0
  45. package/.claude/skills/banana-image/templates/poster.json +22 -0
  46. package/.claude/skills/banana-image/templates/product.json +17 -0
  47. package/.claude/skills/banana-image/templates/social.json +22 -0
  48. package/.claude/skills/banana-image/templates/thumbnail.json +17 -0
  49. package/.claude/skills/brainstorm/SKILL.md +648 -0
  50. package/.claude/skills/bug-detective/SKILL.md +1206 -0
  51. package/.claude/skills/code-patterns/SKILL.md +590 -0
  52. package/.claude/skills/collaborating-with-codex/SKILL.md +174 -0
  53. package/.claude/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  54. package/.claude/skills/collaborating-with-gemini/SKILL.md +194 -0
  55. package/.claude/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  56. package/.claude/skills/crud-development/SKILL.md +649 -0
  57. package/.claude/skills/data-permission/SKILL.md +599 -0
  58. package/.claude/skills/database-ops/SKILL.md +407 -0
  59. package/.claude/skills/error-handler/SKILL.md +371 -0
  60. package/.claude/skills/file-oss-management/SKILL.md +863 -0
  61. package/.claude/skills/git-workflow/SKILL.md +375 -0
  62. package/.claude/skills/json-serialization/SKILL.md +357 -0
  63. package/.claude/skills/leniu-api-development/SKILL.md +803 -0
  64. package/.claude/skills/leniu-architecture-design/SKILL.md +598 -0
  65. package/.claude/skills/leniu-backend-annotations/SKILL.md +664 -0
  66. package/.claude/skills/leniu-code-patterns/SKILL.md +365 -0
  67. package/.claude/skills/leniu-crud-development/SKILL.md +1110 -0
  68. package/.claude/skills/leniu-data-permission/SKILL.md +256 -0
  69. package/.claude/skills/leniu-database-ops/SKILL.md +426 -0
  70. package/.claude/skills/leniu-error-handler/SKILL.md +462 -0
  71. package/.claude/skills/leniu-java-amount-handling/SKILL.md +461 -0
  72. package/.claude/skills/leniu-java-code-style/SKILL.md +510 -0
  73. package/.claude/skills/leniu-java-concurrent/SKILL.md +400 -0
  74. package/.claude/skills/leniu-java-entity/SKILL.md +751 -0
  75. package/.claude/skills/leniu-java-export/SKILL.md +560 -0
  76. package/.claude/skills/leniu-java-logging/SKILL.md +832 -0
  77. package/.claude/skills/leniu-java-mq/SKILL.md +338 -0
  78. package/.claude/skills/leniu-java-mybatis/SKILL.md +640 -0
  79. package/.claude/skills/leniu-java-report-query-param/SKILL.md +291 -0
  80. package/.claude/skills/leniu-java-task/SKILL.md +367 -0
  81. package/.claude/skills/leniu-java-total-line/SKILL.md +195 -0
  82. package/.claude/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  83. package/.claude/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  84. package/.claude/skills/leniu-mealtime/SKILL.md +215 -0
  85. package/.claude/skills/leniu-redis-cache/SKILL.md +316 -0
  86. package/.claude/skills/leniu-security-guard/SKILL.md +520 -0
  87. package/.claude/skills/leniu-utils-toolkit/SKILL.md +380 -0
  88. package/.claude/skills/openspec-apply-change/SKILL.md +156 -0
  89. package/.claude/skills/openspec-archive-change/SKILL.md +114 -0
  90. package/.claude/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  91. package/.claude/skills/openspec-continue-change/SKILL.md +118 -0
  92. package/.claude/skills/openspec-explore/SKILL.md +290 -0
  93. package/.claude/skills/openspec-ff-change/SKILL.md +101 -0
  94. package/.claude/skills/openspec-new-change/SKILL.md +74 -0
  95. package/.claude/skills/openspec-onboard/SKILL.md +529 -0
  96. package/.claude/skills/openspec-sync-specs/SKILL.md +138 -0
  97. package/.claude/skills/openspec-verify-change/SKILL.md +168 -0
  98. package/.claude/skills/performance-doctor/SKILL.md +627 -0
  99. package/.claude/skills/project-navigator/SKILL.md +305 -0
  100. package/.claude/skills/redis-cache/SKILL.md +839 -0
  101. package/.claude/skills/scheduled-jobs/SKILL.md +633 -0
  102. package/.claude/skills/security-guard/SKILL.md +748 -0
  103. package/.claude/skills/sms-mail/SKILL.md +766 -0
  104. package/.claude/skills/social-login/SKILL.md +668 -0
  105. package/.claude/skills/store-pc/SKILL.md +366 -0
  106. package/.claude/skills/task-tracker/SKILL.md +307 -0
  107. package/.claude/skills/tech-decision/SKILL.md +393 -0
  108. package/.claude/skills/tenant-management/SKILL.md +603 -0
  109. package/.claude/skills/test-development/SKILL.md +755 -0
  110. package/.claude/skills/ui-pc/SKILL.md +438 -0
  111. package/.claude/skills/utils-toolkit/SKILL.md +615 -0
  112. package/.claude/skills/websocket-sse/SKILL.md +716 -0
  113. package/.claude/skills/workflow-engine/SKILL.md +676 -0
  114. package/.claude/templates//345/276/205/345/212/236/346/270/205/345/215/225/346/250/241/346/235/277.md +56 -0
  115. package/.claude/templates//351/234/200/346/261/202/346/226/207/346/241/243/346/250/241/346/235/277.md +85 -0
  116. package/.claude/templates//351/241/271/347/233/256/347/212/266/346/200/201/346/250/241/346/235/277.md +43 -0
  117. package/.codex/skills/add-skill/SKILL.md +352 -0
  118. package/.codex/skills/add-todo/SKILL.md +269 -0
  119. package/.codex/skills/api-development/SKILL.md +693 -0
  120. package/.codex/skills/architecture-design/SKILL.md +628 -0
  121. package/.codex/skills/backend-annotations/SKILL.md +664 -0
  122. package/.codex/skills/banana-image/CHANGELOG.md +37 -0
  123. package/.codex/skills/banana-image/README.md +146 -0
  124. package/.codex/skills/banana-image/SKILL.md +164 -0
  125. package/.codex/skills/banana-image/assets/logo.png +0 -0
  126. package/.codex/skills/banana-image/references/advanced-usage.md +189 -0
  127. package/.codex/skills/banana-image/scripts/apply_template.py +125 -0
  128. package/.codex/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  129. package/.codex/skills/banana-image/scripts/batch_prep.py +82 -0
  130. package/.codex/skills/banana-image/scripts/package-lock.json +1437 -0
  131. package/.codex/skills/banana-image/scripts/package.json +18 -0
  132. package/.codex/skills/banana-image/scripts/requirements.txt +10 -0
  133. package/.codex/skills/banana-image/templates/poster.json +22 -0
  134. package/.codex/skills/banana-image/templates/product.json +17 -0
  135. package/.codex/skills/banana-image/templates/social.json +22 -0
  136. package/.codex/skills/banana-image/templates/thumbnail.json +17 -0
  137. package/.codex/skills/brainstorm/SKILL.md +648 -0
  138. package/.codex/skills/bug-detective/SKILL.md +1206 -0
  139. package/.codex/skills/check/SKILL.md +367 -0
  140. package/.codex/skills/code-patterns/SKILL.md +442 -0
  141. package/.codex/skills/collaborating-with-codex/SKILL.md +174 -0
  142. package/.codex/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  143. package/.codex/skills/collaborating-with-gemini/SKILL.md +194 -0
  144. package/.codex/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  145. package/.codex/skills/crud/SKILL.md +265 -0
  146. package/.codex/skills/crud-development/SKILL.md +637 -0
  147. package/.codex/skills/data-permission/SKILL.md +591 -0
  148. package/.codex/skills/database-ops/SKILL.md +553 -0
  149. package/.codex/skills/dev/SKILL.md +187 -0
  150. package/.codex/skills/error-handler/SKILL.md +361 -0
  151. package/.codex/skills/file-oss-management/SKILL.md +863 -0
  152. package/.codex/skills/git-workflow/SKILL.md +375 -0
  153. package/.codex/skills/init-docs/SKILL.md +194 -0
  154. package/.codex/skills/json-serialization/SKILL.md +357 -0
  155. package/.codex/skills/leniu-api-development/SKILL.md +803 -0
  156. package/.codex/skills/leniu-architecture-design/SKILL.md +594 -0
  157. package/.codex/skills/leniu-backend-annotations/SKILL.md +662 -0
  158. package/.codex/skills/leniu-code-patterns/SKILL.md +365 -0
  159. package/.codex/skills/leniu-crud-development/SKILL.md +1110 -0
  160. package/.codex/skills/leniu-data-permission/SKILL.md +256 -0
  161. package/.codex/skills/leniu-database-ops/SKILL.md +426 -0
  162. package/.codex/skills/leniu-error-handler/SKILL.md +462 -0
  163. package/.codex/skills/leniu-java-amount-handling/SKILL.md +461 -0
  164. package/.codex/skills/leniu-java-code-style/SKILL.md +510 -0
  165. package/.codex/skills/leniu-java-concurrent/SKILL.md +400 -0
  166. package/.codex/skills/leniu-java-entity/SKILL.md +751 -0
  167. package/.codex/skills/leniu-java-export/SKILL.md +560 -0
  168. package/.codex/skills/leniu-java-logging/SKILL.md +832 -0
  169. package/.codex/skills/leniu-java-mq/SKILL.md +338 -0
  170. package/.codex/skills/leniu-java-mybatis/SKILL.md +640 -0
  171. package/.codex/skills/leniu-java-report-query-param/SKILL.md +291 -0
  172. package/.codex/skills/leniu-java-task/SKILL.md +367 -0
  173. package/.codex/skills/leniu-java-total-line/SKILL.md +195 -0
  174. package/.codex/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  175. package/.codex/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  176. package/.codex/skills/leniu-mealtime/SKILL.md +215 -0
  177. package/.codex/skills/leniu-redis-cache/SKILL.md +316 -0
  178. package/.codex/skills/leniu-security-guard/SKILL.md +520 -0
  179. package/.codex/skills/leniu-utils-toolkit/SKILL.md +378 -0
  180. package/.codex/skills/next/SKILL.md +137 -0
  181. package/.codex/skills/openspec-apply-change/SKILL.md +156 -0
  182. package/.codex/skills/openspec-archive-change/SKILL.md +114 -0
  183. package/.codex/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  184. package/.codex/skills/openspec-continue-change/SKILL.md +118 -0
  185. package/.codex/skills/openspec-explore/SKILL.md +290 -0
  186. package/.codex/skills/openspec-ff-change/SKILL.md +101 -0
  187. package/.codex/skills/openspec-new-change/SKILL.md +74 -0
  188. package/.codex/skills/openspec-onboard/SKILL.md +529 -0
  189. package/.codex/skills/openspec-sync-specs/SKILL.md +138 -0
  190. package/.codex/skills/openspec-verify-change/SKILL.md +168 -0
  191. package/.codex/skills/performance-doctor/SKILL.md +627 -0
  192. package/.codex/skills/progress/SKILL.md +193 -0
  193. package/.codex/skills/project-navigator/SKILL.md +286 -0
  194. package/.codex/skills/redis-cache/SKILL.md +829 -0
  195. package/.codex/skills/scheduled-jobs/SKILL.md +633 -0
  196. package/.codex/skills/security-guard/SKILL.md +739 -0
  197. package/.codex/skills/sms-mail/SKILL.md +766 -0
  198. package/.codex/skills/social-login/SKILL.md +668 -0
  199. package/.codex/skills/start/SKILL.md +154 -0
  200. package/.codex/skills/store-pc/SKILL.md +491 -0
  201. package/.codex/skills/sync/SKILL.md +149 -0
  202. package/.codex/skills/task-tracker/SKILL.md +307 -0
  203. package/.codex/skills/tech-decision/SKILL.md +393 -0
  204. package/.codex/skills/tenant-management/SKILL.md +603 -0
  205. package/.codex/skills/test-development/SKILL.md +755 -0
  206. package/.codex/skills/ui-pc/SKILL.md +475 -0
  207. package/.codex/skills/update-status/SKILL.md +159 -0
  208. package/.codex/skills/utils-toolkit/SKILL.md +593 -0
  209. package/.codex/skills/websocket-sse/SKILL.md +716 -0
  210. package/.codex/skills/workflow-engine/SKILL.md +676 -0
  211. package/.cursor/agents/code-reviewer.md +139 -0
  212. package/.cursor/agents/project-manager.md +159 -0
  213. package/.cursor/commands/opsx-apply.md +152 -0
  214. package/.cursor/commands/opsx-archive.md +157 -0
  215. package/.cursor/commands/opsx-bulk-archive.md +242 -0
  216. package/.cursor/commands/opsx-continue.md +114 -0
  217. package/.cursor/commands/opsx-explore.md +174 -0
  218. package/.cursor/commands/opsx-ff.md +94 -0
  219. package/.cursor/commands/opsx-new.md +69 -0
  220. package/.cursor/commands/opsx-onboard.md +525 -0
  221. package/.cursor/commands/opsx-sync.md +134 -0
  222. package/.cursor/commands/opsx-verify.md +164 -0
  223. package/.cursor/mcp.json +22 -0
  224. package/.cursor/skills/add-skill/SKILL.md +352 -0
  225. package/.cursor/skills/api-development/SKILL.md +560 -0
  226. package/.cursor/skills/architecture-design/SKILL.md +756 -0
  227. package/.cursor/skills/backend-annotations/SKILL.md +674 -0
  228. package/.cursor/skills/banana-image/CHANGELOG.md +37 -0
  229. package/.cursor/skills/banana-image/README.md +146 -0
  230. package/.cursor/skills/banana-image/SKILL.md +164 -0
  231. package/.cursor/skills/banana-image/assets/logo.png +0 -0
  232. package/.cursor/skills/banana-image/references/advanced-usage.md +189 -0
  233. package/.cursor/skills/banana-image/scripts/apply_template.py +125 -0
  234. package/.cursor/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  235. package/.cursor/skills/banana-image/scripts/batch_prep.py +82 -0
  236. package/.cursor/skills/banana-image/scripts/package-lock.json +1437 -0
  237. package/.cursor/skills/banana-image/scripts/package.json +18 -0
  238. package/.cursor/skills/banana-image/scripts/requirements.txt +10 -0
  239. package/.cursor/skills/banana-image/templates/poster.json +22 -0
  240. package/.cursor/skills/banana-image/templates/product.json +17 -0
  241. package/.cursor/skills/banana-image/templates/social.json +22 -0
  242. package/.cursor/skills/banana-image/templates/thumbnail.json +17 -0
  243. package/.cursor/skills/brainstorm/SKILL.md +648 -0
  244. package/.cursor/skills/bug-detective/SKILL.md +1206 -0
  245. package/.cursor/skills/code-patterns/SKILL.md +590 -0
  246. package/.cursor/skills/collaborating-with-codex/SKILL.md +174 -0
  247. package/.cursor/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  248. package/.cursor/skills/collaborating-with-gemini/SKILL.md +194 -0
  249. package/.cursor/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  250. package/.cursor/skills/crud-development/SKILL.md +649 -0
  251. package/.cursor/skills/data-permission/SKILL.md +599 -0
  252. package/.cursor/skills/database-ops/SKILL.md +407 -0
  253. package/.cursor/skills/error-handler/SKILL.md +371 -0
  254. package/.cursor/skills/file-oss-management/SKILL.md +863 -0
  255. package/.cursor/skills/git-workflow/SKILL.md +375 -0
  256. package/.cursor/skills/json-serialization/SKILL.md +357 -0
  257. package/.cursor/skills/leniu-api-development/SKILL.md +803 -0
  258. package/.cursor/skills/leniu-architecture-design/SKILL.md +598 -0
  259. package/.cursor/skills/leniu-backend-annotations/SKILL.md +664 -0
  260. package/.cursor/skills/leniu-code-patterns/SKILL.md +365 -0
  261. package/.cursor/skills/leniu-crud-development/SKILL.md +1110 -0
  262. package/.cursor/skills/leniu-data-permission/SKILL.md +256 -0
  263. package/.cursor/skills/leniu-database-ops/SKILL.md +426 -0
  264. package/.cursor/skills/leniu-error-handler/SKILL.md +462 -0
  265. package/.cursor/skills/leniu-java-amount-handling/SKILL.md +461 -0
  266. package/.cursor/skills/leniu-java-code-style/SKILL.md +510 -0
  267. package/.cursor/skills/leniu-java-concurrent/SKILL.md +400 -0
  268. package/.cursor/skills/leniu-java-entity/SKILL.md +751 -0
  269. package/.cursor/skills/leniu-java-export/SKILL.md +560 -0
  270. package/.cursor/skills/leniu-java-logging/SKILL.md +832 -0
  271. package/.cursor/skills/leniu-java-mq/SKILL.md +338 -0
  272. package/.cursor/skills/leniu-java-mybatis/SKILL.md +640 -0
  273. package/.cursor/skills/leniu-java-report-query-param/SKILL.md +291 -0
  274. package/.cursor/skills/leniu-java-task/SKILL.md +367 -0
  275. package/.cursor/skills/leniu-java-total-line/SKILL.md +195 -0
  276. package/.cursor/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  277. package/.cursor/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  278. package/.cursor/skills/leniu-mealtime/SKILL.md +215 -0
  279. package/.cursor/skills/leniu-redis-cache/SKILL.md +316 -0
  280. package/.cursor/skills/leniu-security-guard/SKILL.md +520 -0
  281. package/.cursor/skills/leniu-utils-toolkit/SKILL.md +380 -0
  282. package/.cursor/skills/openspec-apply-change/SKILL.md +156 -0
  283. package/.cursor/skills/openspec-archive-change/SKILL.md +114 -0
  284. package/.cursor/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  285. package/.cursor/skills/openspec-continue-change/SKILL.md +118 -0
  286. package/.cursor/skills/openspec-explore/SKILL.md +290 -0
  287. package/.cursor/skills/openspec-ff-change/SKILL.md +101 -0
  288. package/.cursor/skills/openspec-new-change/SKILL.md +74 -0
  289. package/.cursor/skills/openspec-onboard/SKILL.md +529 -0
  290. package/.cursor/skills/openspec-sync-specs/SKILL.md +138 -0
  291. package/.cursor/skills/openspec-verify-change/SKILL.md +168 -0
  292. package/.cursor/skills/performance-doctor/SKILL.md +627 -0
  293. package/.cursor/skills/project-navigator/SKILL.md +305 -0
  294. package/.cursor/skills/redis-cache/SKILL.md +839 -0
  295. package/.cursor/skills/scheduled-jobs/SKILL.md +633 -0
  296. package/.cursor/skills/security-guard/SKILL.md +748 -0
  297. package/.cursor/skills/sms-mail/SKILL.md +766 -0
  298. package/.cursor/skills/social-login/SKILL.md +668 -0
  299. package/.cursor/skills/store-pc/SKILL.md +366 -0
  300. package/.cursor/skills/task-tracker/SKILL.md +307 -0
  301. package/.cursor/skills/tech-decision/SKILL.md +393 -0
  302. package/.cursor/skills/tenant-management/SKILL.md +603 -0
  303. package/.cursor/skills/test-development/SKILL.md +755 -0
  304. package/.cursor/skills/ui-pc/SKILL.md +438 -0
  305. package/.cursor/skills/utils-toolkit/SKILL.md +615 -0
  306. package/.cursor/skills/websocket-sse/SKILL.md +716 -0
  307. package/.cursor/skills/workflow-engine/SKILL.md +676 -0
  308. package/AGENTS.md +669 -0
  309. package/CLAUDE.md +205 -0
  310. package/README.md +205 -0
  311. package/bin/index.js +179 -0
  312. package/init.sh +178 -0
  313. package/package.json +27 -0
@@ -0,0 +1,755 @@
1
+ ---
2
+ name: test-development
3
+ description: |
4
+ 测试开发技能,编写单元测试、集成测试、Controller测试。基于 JUnit5 + Spring Boot Test + Mockito 标准测试框架。
5
+
6
+ 触发场景:
7
+ - 编写单元测试(工具类、枚举、POJO)
8
+ - 编写 Spring 集成测试(Service、Controller、Mapper)
9
+ - Mock 外部依赖
10
+ - 参数化测试
11
+ - 测试数据构造
12
+ - 测试覆盖率提升
13
+
14
+ 触发词:测试、单元测试、集成测试、@Test、JUnit5、JUnit、Mockito、Mock、断言、test、测试用例、测试覆盖率、测试数据、@SpringBootTest、@Mock、@MockBean、Assertions、测试类、测试方法、@ParameterizedTest、参数化测试、@BeforeEach、@AfterEach、@DisplayName
15
+
16
+ 注意:本项目使用标准的 JUnit5 + Spring Boot Test,没有自定义测试基类。
17
+ ---
18
+
19
+ # 测试开发规范
20
+
21
+ > **核心原则**:本项目使用标准的 JUnit5 + Spring Boot Test,根据测试场景选择是否启动 Spring 容器!
22
+
23
+ ## 测试分层策略
24
+
25
+ | 层次 | 测试类型 | 是否启动 Spring | 特点 | 执行速度 |
26
+ |------|---------|----------------|------|---------|
27
+ | **单元测试** | 工具类/枚举/POJO | ❌ 否 | 纯 JUnit5,无依赖注入 | < 1s |
28
+ | **集成测试** | Service/Controller/Mapper | ✅ 是 | 使用 `@SpringBootTest`,完整 Spring 容器 | 5-10s |
29
+
30
+ ## 测试文件位置
31
+
32
+ | 类型 | 位置 | 示例 |
33
+ |------|------|------|
34
+ | 测试类 | `src/test/java` | `ruoyi-admin/src/test/java/org/dromara/test/` |
35
+ | 测试资源 | `src/test/resources` | 测试配置文件、测试数据等 |
36
+
37
+ ## 核心依赖
38
+
39
+ Spring Boot Test 已包含在 `spring-boot-starter-test` 中,包含:
40
+ - **JUnit 5**:测试框架
41
+ - **Mockito**:Mock 框架
42
+ - **Spring Test**:Spring 测试支持
43
+
44
+ ```xml
45
+ <!-- 在业务模块的 pom.xml 中已包含 -->
46
+ <dependency>
47
+ <groupId>org.springframework.boot</groupId>
48
+ <artifactId>spring-boot-starter-test</artifactId>
49
+ <scope>test</scope>
50
+ </dependency>
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 1. 单元测试(纯 JUnit5)
56
+
57
+ **适用场景:** 工具类、枚举类、POJO、算法逻辑(无需 Spring 容器)
58
+
59
+ **特点:** 不使用 `@SpringBootTest`,执行速度快,适合纯逻辑测试。
60
+
61
+ ```java
62
+ package org.dromara.test;
63
+
64
+ import org.junit.jupiter.api.Assertions;
65
+ import org.junit.jupiter.api.DisplayName;
66
+ import org.junit.jupiter.api.Test;
67
+
68
+ /**
69
+ * 断言单元测试案例
70
+ * 参考:ruoyi-admin/src/test/java/org/dromara/test/AssertUnitTest.java
71
+ */
72
+ @DisplayName("断言单元测试案例")
73
+ public class AssertUnitTest {
74
+
75
+ @Test
76
+ @DisplayName("测试 assertEquals 方法")
77
+ public void testAssertEquals() {
78
+ Assertions.assertEquals("666", new String("666"));
79
+ Assertions.assertNotEquals("777", new String("666"));
80
+ }
81
+
82
+ @Test
83
+ @DisplayName("测试 assertTrue 方法")
84
+ public void testAssertTrue() {
85
+ Assertions.assertTrue(true);
86
+ Assertions.assertFalse(false);
87
+ }
88
+
89
+ @Test
90
+ @DisplayName("测试 assertNull 方法")
91
+ public void testAssertNull() {
92
+ Assertions.assertNull(null);
93
+ Assertions.assertNotNull("not null");
94
+ }
95
+ }
96
+ ```
97
+
98
+ **常用断言方法:**
99
+
100
+ ```java
101
+ // 相等性断言
102
+ Assertions.assertEquals(expected, actual);
103
+ Assertions.assertNotEquals(expected, actual);
104
+
105
+ // 同一性断言
106
+ Assertions.assertSame(obj1, obj2);
107
+ Assertions.assertNotSame(obj1, obj2);
108
+
109
+ // 布尔断言
110
+ Assertions.assertTrue(condition);
111
+ Assertions.assertFalse(condition);
112
+
113
+ // 空值断言
114
+ Assertions.assertNull(object);
115
+ Assertions.assertNotNull(object);
116
+
117
+ // 异常断言
118
+ Assertions.assertThrows(Exception.class, () -> {
119
+ // 会抛出异常的代码
120
+ });
121
+ ```
122
+
123
+ ---
124
+
125
+ ## 2. Spring 集成测试(@SpringBootTest)
126
+
127
+ **适用场景:** 需要 Spring 容器的测试(Service、Controller、Mapper、需要依赖注入的场景)
128
+
129
+ **特点:** 使用 `@SpringBootTest` 启动完整的 Spring 容器,可以使用 `@Autowired` 注入 Bean。
130
+
131
+ **重要提示:** `@SpringBootTest` 只能在 SpringBoot 主包下使用,需包含 main 方法与 yml 配置文件。
132
+
133
+ ```java
134
+ package org.dromara.test;
135
+
136
+ import org.dromara.common.web.config.properties.CaptchaProperties;
137
+ import org.junit.jupiter.api.*;
138
+ import org.springframework.beans.factory.annotation.Autowired;
139
+ import org.springframework.boot.test.context.SpringBootTest;
140
+
141
+ import java.util.concurrent.TimeUnit;
142
+
143
+ /**
144
+ * Spring 集成测试案例
145
+ * 参考:ruoyi-admin/src/test/java/org/dromara/test/DemoUnitTest.java
146
+ */
147
+ @SpringBootTest
148
+ @DisplayName("单元测试案例")
149
+ public class DemoUnitTest {
150
+
151
+ @Autowired
152
+ private CaptchaProperties captchaProperties;
153
+
154
+ @Test
155
+ @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
156
+ public void testTest() {
157
+ System.out.println(captchaProperties);
158
+ Assertions.assertNotNull(captchaProperties);
159
+ }
160
+
161
+ @Test
162
+ @DisplayName("测试 @Disabled 注解")
163
+ @Disabled // 禁用此测试
164
+ public void testDisabled() {
165
+ System.out.println("此测试被禁用");
166
+ }
167
+
168
+ @Test
169
+ @DisplayName("测试 @Timeout 注解")
170
+ @Timeout(value = 2L, unit = TimeUnit.SECONDS)
171
+ public void testTimeout() throws InterruptedException {
172
+ Thread.sleep(1000); // 1秒,不会超时
173
+ }
174
+
175
+ @RepeatedTest(3)
176
+ @DisplayName("测试 @RepeatedTest 注解")
177
+ public void testRepeatedTest() {
178
+ System.out.println("重复测试");
179
+ }
180
+
181
+ @BeforeAll
182
+ public static void testBeforeAll() {
183
+ System.out.println("@BeforeAll - 所有测试前执行一次");
184
+ }
185
+
186
+ @BeforeEach
187
+ public void testBeforeEach() {
188
+ System.out.println("@BeforeEach - 每个测试前执行");
189
+ }
190
+
191
+ @AfterEach
192
+ public void testAfterEach() {
193
+ System.out.println("@AfterEach - 每个测试后执行");
194
+ }
195
+
196
+ @AfterAll
197
+ public static void testAfterAll() {
198
+ System.out.println("@AfterAll - 所有测试后执行一次");
199
+ }
200
+ }
201
+ ```
202
+
203
+ ---
204
+
205
+ ## 3. Service 测试(@SpringBootTest + @Transactional)
206
+
207
+ **适用场景:** Service 层业务逻辑测试,需要数据库操作
208
+
209
+ **特点:** 使用 `@SpringBootTest` + `@Transactional`,测试后自动回滚,不污染数据库。
210
+
211
+ ```java
212
+ package org.dromara.demo.service;
213
+
214
+ import org.dromara.demo.domain.bo.TestDemoBo;
215
+ import org.dromara.demo.domain.vo.TestDemoVo;
216
+ import org.junit.jupiter.api.*;
217
+ import org.springframework.beans.factory.annotation.Autowired;
218
+ import org.springframework.boot.test.context.SpringBootTest;
219
+ import org.springframework.transaction.annotation.Transactional;
220
+
221
+ /**
222
+ * Service 层测试示例
223
+ */
224
+ @SpringBootTest
225
+ @Transactional // 测试后自动回滚
226
+ @DisplayName("TestDemo Service 测试")
227
+ public class TestDemoServiceTest {
228
+
229
+ @Autowired
230
+ private ITestDemoService testDemoService;
231
+
232
+ @Test
233
+ @DisplayName("测试添加数据")
234
+ public void testAdd() {
235
+ // Arrange - 准备数据
236
+ TestDemoBo bo = new TestDemoBo();
237
+ bo.setDeptId(103L);
238
+ bo.setUserId(1L);
239
+ bo.setOrderNum(1);
240
+ bo.setTestKey("测试数据");
241
+ bo.setValue("test_value");
242
+
243
+ // Act - 执行操作
244
+ Boolean result = testDemoService.insertByBo(bo);
245
+
246
+ // Assert - 验证结果
247
+ Assertions.assertTrue(result);
248
+ }
249
+
250
+ @Test
251
+ @DisplayName("测试查询详情")
252
+ public void testGetById() {
253
+ // 先添加测试数据
254
+ TestDemoBo bo = new TestDemoBo();
255
+ bo.setDeptId(103L);
256
+ bo.setUserId(1L);
257
+ bo.setOrderNum(2);
258
+ bo.setTestKey("测试查询");
259
+ bo.setValue("test_query");
260
+ testDemoService.insertByBo(bo);
261
+
262
+ // 查询详情
263
+ TestDemoVo vo = testDemoService.queryById(bo.getId());
264
+
265
+ // 断言
266
+ Assertions.assertNotNull(vo);
267
+ Assertions.assertEquals("测试查询", vo.getTestKey());
268
+ }
269
+
270
+ @Test
271
+ @DisplayName("测试更新数据")
272
+ public void testUpdate() {
273
+ // 先添加测试数据
274
+ TestDemoBo bo = new TestDemoBo();
275
+ bo.setDeptId(103L);
276
+ bo.setUserId(1L);
277
+ bo.setOrderNum(3);
278
+ bo.setTestKey("原始名称");
279
+ bo.setValue("original");
280
+ testDemoService.insertByBo(bo);
281
+
282
+ // 更新数据
283
+ bo.setTestKey("更新后名称");
284
+ Boolean result = testDemoService.updateByBo(bo);
285
+
286
+ // 验证
287
+ Assertions.assertTrue(result);
288
+ TestDemoVo vo = testDemoService.queryById(bo.getId());
289
+ Assertions.assertEquals("更新后名称", vo.getTestKey());
290
+ }
291
+ }
292
+ ```
293
+
294
+ **Mock 外部依赖示例:**
295
+
296
+ ```java
297
+ @SpringBootTest
298
+ @DisplayName("订单服务测试")
299
+ public class OrderServiceTest {
300
+
301
+ @Autowired
302
+ private IOrderService orderService;
303
+
304
+ @MockBean // Mock 支付服务
305
+ private IPaymentService paymentService;
306
+
307
+ @Test
308
+ @DisplayName("测试订单支付(Mock 支付服务)")
309
+ public void testPayOrder() {
310
+ Long orderId = 123L;
311
+
312
+ // Mock 行为
313
+ Mockito.when(paymentService.pay(orderId)).thenReturn(true);
314
+
315
+ // 执行
316
+ Boolean success = orderService.payOrder(orderId);
317
+
318
+ // 断言
319
+ Assertions.assertTrue(success);
320
+ Mockito.verify(paymentService, Mockito.times(1)).pay(orderId);
321
+ }
322
+ }
323
+ ```
324
+
325
+ ---
326
+
327
+ ## 4. Controller 测试(@SpringBootTest + MockMvc)
328
+
329
+ **适用场景:** HTTP 接口测试,完整请求链路
330
+
331
+ **特点:** 使用 `@SpringBootTest` + `@AutoConfigureMockMvc`,模拟 HTTP 请求。
332
+
333
+ ```java
334
+ package org.dromara.demo.controller;
335
+
336
+ import org.junit.jupiter.api.*;
337
+ import org.springframework.beans.factory.annotation.Autowired;
338
+ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
339
+ import org.springframework.boot.test.context.SpringBootTest;
340
+ import org.springframework.http.MediaType;
341
+ import org.springframework.test.web.servlet.MockMvc;
342
+
343
+ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
344
+ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
345
+
346
+ /**
347
+ * Controller 层测试示例
348
+ */
349
+ @SpringBootTest
350
+ @AutoConfigureMockMvc
351
+ @DisplayName("TestDemo Controller 测试")
352
+ public class TestDemoControllerTest {
353
+
354
+ @Autowired
355
+ private MockMvc mockMvc;
356
+
357
+ @Test
358
+ @DisplayName("测试分页查询")
359
+ public void testPageList() throws Exception {
360
+ mockMvc.perform(get("/demo/demo/list")
361
+ .param("pageNum", "1")
362
+ .param("pageSize", "10"))
363
+ .andExpect(status().isOk())
364
+ .andExpect(jsonPath("$.code").value(200));
365
+ }
366
+
367
+ @Test
368
+ @DisplayName("测试查询详情")
369
+ public void testGetById() throws Exception {
370
+ mockMvc.perform(get("/demo/demo/1"))
371
+ .andExpect(status().isOk())
372
+ .andExpect(jsonPath("$.code").value(200));
373
+ }
374
+
375
+ @Test
376
+ @DisplayName("测试添加数据")
377
+ public void testAdd() throws Exception {
378
+ String requestBody = """
379
+ {
380
+ "deptId": 103,
381
+ "userId": 1,
382
+ "orderNum": 1,
383
+ "testKey": "测试数据",
384
+ "value": "test_value"
385
+ }
386
+ """;
387
+
388
+ mockMvc.perform(post("/demo/demo")
389
+ .contentType(MediaType.APPLICATION_JSON)
390
+ .content(requestBody))
391
+ .andExpect(status().isOk())
392
+ .andExpect(jsonPath("$.code").value(200));
393
+ }
394
+
395
+ @Test
396
+ @DisplayName("测试更新数据")
397
+ public void testUpdate() throws Exception {
398
+ String requestBody = """
399
+ {
400
+ "id": 1,
401
+ "deptId": 103,
402
+ "userId": 1,
403
+ "orderNum": 1,
404
+ "testKey": "更新后数据",
405
+ "value": "updated_value"
406
+ }
407
+ """;
408
+
409
+ mockMvc.perform(put("/demo/demo")
410
+ .contentType(MediaType.APPLICATION_JSON)
411
+ .content(requestBody))
412
+ .andExpect(status().isOk())
413
+ .andExpect(jsonPath("$.code").value(200));
414
+ }
415
+
416
+ @Test
417
+ @DisplayName("测试删除数据")
418
+ public void testDelete() throws Exception {
419
+ mockMvc.perform(delete("/demo/demo/1,2,3"))
420
+ .andExpect(status().isOk())
421
+ .andExpect(jsonPath("$.code").value(200));
422
+ }
423
+ }
424
+ ```
425
+
426
+ ---
427
+
428
+ ## 5. 参数化测试
429
+
430
+ **适用场景:** 需要用多组数据测试同一个方法
431
+
432
+ **特点:** 使用 `@ParameterizedTest` 替代 `@Test`,配合数据源注解提供测试数据。
433
+
434
+ ```java
435
+ package org.dromara.test;
436
+
437
+ import org.dromara.common.core.enums.UserType;
438
+ import org.junit.jupiter.api.*;
439
+ import org.junit.jupiter.params.ParameterizedTest;
440
+ import org.junit.jupiter.params.provider.*;
441
+
442
+ import java.util.ArrayList;
443
+ import java.util.List;
444
+ import java.util.stream.Stream;
445
+
446
+ /**
447
+ * 参数化测试案例
448
+ * 参考:ruoyi-admin/src/test/java/org/dromara/test/ParamUnitTest.java
449
+ */
450
+ @DisplayName("带参数单元测试案例")
451
+ public class ParamUnitTest {
452
+
453
+ @ParameterizedTest
454
+ @DisplayName("测试 @ValueSource 注解")
455
+ @ValueSource(strings = {"t1", "t2", "t3"})
456
+ public void testValueSource(String str) {
457
+ System.out.println(str);
458
+ Assertions.assertNotNull(str);
459
+ }
460
+
461
+ @ParameterizedTest
462
+ @DisplayName("测试 @NullSource 注解")
463
+ @NullSource
464
+ public void testNullSource(String str) {
465
+ System.out.println(str);
466
+ Assertions.assertNull(str);
467
+ }
468
+
469
+ @ParameterizedTest
470
+ @DisplayName("测试 @EnumSource 注解")
471
+ @EnumSource(UserType.class)
472
+ public void testEnumSource(UserType type) {
473
+ System.out.println(type.getUserType());
474
+ Assertions.assertNotNull(type);
475
+ }
476
+
477
+ @ParameterizedTest
478
+ @DisplayName("测试 @MethodSource 注解")
479
+ @MethodSource("getParam")
480
+ public void testMethodSource(String str) {
481
+ System.out.println(str);
482
+ Assertions.assertNotNull(str);
483
+ }
484
+
485
+ public static Stream<String> getParam() {
486
+ List<String> list = new ArrayList<>();
487
+ list.add("t1");
488
+ list.add("t2");
489
+ list.add("t3");
490
+ return list.stream();
491
+ }
492
+
493
+ @BeforeEach
494
+ public void testBeforeEach() {
495
+ System.out.println("@BeforeEach - 每个测试前执行");
496
+ }
497
+
498
+ @AfterEach
499
+ public void testAfterEach() {
500
+ System.out.println("@AfterEach - 每个测试后执行");
501
+ }
502
+ }
503
+ ```
504
+
505
+ **更多参数化测试示例:**
506
+
507
+ ```java
508
+ // CSV 数据源
509
+ @ParameterizedTest
510
+ @DisplayName("测试 CSV 数据")
511
+ @CsvSource({
512
+ "1, Banner, 横幅广告",
513
+ "2, Popup, 弹窗广告"
514
+ })
515
+ public void testCsvData(String code, String name, String desc) {
516
+ Assertions.assertNotNull(code);
517
+ Assertions.assertNotNull(name);
518
+ }
519
+
520
+ // 多个值源
521
+ @ParameterizedTest
522
+ @DisplayName("测试多个整数")
523
+ @ValueSource(ints = {1, 2, 3, 4, 5})
524
+ public void testIntValues(int num) {
525
+ Assertions.assertTrue(num > 0);
526
+ }
527
+
528
+ // 空值和空字符串
529
+ @ParameterizedTest
530
+ @DisplayName("测试空值")
531
+ @NullAndEmptySource
532
+ @ValueSource(strings = {" ", "\t", "\n"})
533
+ public void testBlankStrings(String input) {
534
+ Assertions.assertTrue(input == null || input.isBlank());
535
+ }
536
+ ```
537
+
538
+ ---
539
+
540
+ ## 6. 测试标签和分组(@Tag)
541
+
542
+ **适用场景:** 需要对测试进行分组,选择性运行某些测试
543
+
544
+ **特点:** 使用 `@Tag` 注解标记测试,可以按标签过滤执行。
545
+
546
+ ```java
547
+ package org.dromara.test;
548
+
549
+ import org.junit.jupiter.api.*;
550
+ import org.springframework.boot.test.context.SpringBootTest;
551
+
552
+ /**
553
+ * 标签单元测试案例
554
+ * 参考:ruoyi-admin/src/test/java/org/dromara/test/TagUnitTest.java
555
+ */
556
+ @SpringBootTest
557
+ @DisplayName("标签单元测试案例")
558
+ public class TagUnitTest {
559
+
560
+ @Test
561
+ @Tag("dev")
562
+ @DisplayName("测试 @Tag dev")
563
+ public void testTagDev() {
564
+ System.out.println("dev 环境测试");
565
+ }
566
+
567
+ @Test
568
+ @Tag("prod")
569
+ @DisplayName("测试 @Tag prod")
570
+ public void testTagProd() {
571
+ System.out.println("prod 环境测试");
572
+ }
573
+
574
+ @Test
575
+ @Tag("local")
576
+ @DisplayName("测试 @Tag local")
577
+ public void testTagLocal() {
578
+ System.out.println("local 环境测试");
579
+ }
580
+
581
+ @Test
582
+ @Tag("exclude")
583
+ @DisplayName("测试 @Tag exclude")
584
+ public void testTagExclude() {
585
+ System.out.println("排除的测试");
586
+ }
587
+
588
+ @BeforeEach
589
+ public void testBeforeEach() {
590
+ System.out.println("@BeforeEach - 每个测试前执行");
591
+ }
592
+
593
+ @AfterEach
594
+ public void testAfterEach() {
595
+ System.out.println("@AfterEach - 每个测试后执行");
596
+ }
597
+ }
598
+ ```
599
+
600
+ **运行指定标签的测试:**
601
+
602
+ ```bash
603
+ # Maven 运行指定标签的测试
604
+ mvn test -Dgroups=dev
605
+
606
+ # Maven 排除指定标签的测试
607
+ mvn test -DexcludedGroups=exclude
608
+
609
+ # IDEA 中配置标签过滤
610
+ # Run → Edit Configurations → Test kind: Tags → Tag expression: dev
611
+ ```
612
+
613
+ ---
614
+
615
+ ## 开发检查清单
616
+
617
+ ### 测试文件规范
618
+
619
+ - [ ] **测试类命名**:`{被测试类名}Test`(如 `StringUtilsTest`、`DemoUnitTest`)
620
+ - [ ] **测试类位置**:`src/test/java` 目录下,包路径与源码一致
621
+ - [ ] **测试方法命名**:`test{功能}`(如 `testIsBlank`、`testAdd`)
622
+ - [ ] **使用 @DisplayName**:为测试类和方法添加中文描述
623
+
624
+ ### 注解选择
625
+
626
+ - [ ] **纯单元测试**:不使用 `@SpringBootTest`,适合工具类/枚举/POJO
627
+ - [ ] **集成测试**:使用 `@SpringBootTest`,适合 Service/Controller/Mapper
628
+ - [ ] **Service 测试**:使用 `@SpringBootTest` + `@Transactional`,测试后自动回滚
629
+ - [ ] **Controller 测试**:使用 `@SpringBootTest` + `@AutoConfigureMockMvc`
630
+
631
+ ### 断言规范
632
+
633
+ - [ ] **使用 JUnit5 Assertions**:`Assertions.assertEquals()` / `Assertions.assertTrue()` 等
634
+ - [ ] **异常断言**:`Assertions.assertThrows(Exception.class, () -> {...})`
635
+ - [ ] **空值断言**:`Assertions.assertNull()` / `Assertions.assertNotNull()`
636
+
637
+ ### Mock 规范
638
+
639
+ - [ ] **Spring 测试**:使用 `@MockBean` Mock Spring Bean
640
+ - [ ] **单元测试**:使用 `@Mock` / `@InjectMocks`(Mockito)
641
+ - [ ] **验证调用**:使用 `Mockito.verify()` 验证 Mock 方法被调用
642
+
643
+ ---
644
+
645
+ ## 常见错误
646
+
647
+ | 错误写法 | 正确写法 | 原因 |
648
+ |---------|---------|------|
649
+ | 测试类在 `src/main/java` | 测试类在 `src/test/java` | 测试代码不应打包到生产环境 |
650
+ | `@Test public void test()` | `@Test @DisplayName("xxx") public void testXxx()` | 缺少描述和命名规范 |
651
+ | 忘记添加 `@SpringBootTest` | 需要依赖注入时添加 `@SpringBootTest` | 无法注入 Spring Bean |
652
+ | `@SpringBootTest` 在非主包下 | 测试类必须在主包下(包含 main 方法) | `@SpringBootTest` 需要找到主类 |
653
+ | Mock 后不验证调用 | `Mockito.verify(mockObj).method()` | Mock 应验证调用次数和参数 |
654
+ | 测试方法相互依赖 | 每个测试方法独立 | 测试应独立可并行执行 |
655
+ | Service 测试不加 `@Transactional` | 使用 `@Transactional` 自动回滚 | 避免污染数据库 |
656
+ | 硬编码测试数据 | 使用变量或常量 | 提高可维护性 |
657
+
658
+ ---
659
+
660
+ ## 异常测试
661
+
662
+ **适用场景:** 测试方法是否正确抛出异常
663
+
664
+ ```java
665
+ import org.dromara.common.core.exception.ServiceException;
666
+ import org.junit.jupiter.api.Assertions;
667
+ import org.junit.jupiter.api.DisplayName;
668
+ import org.junit.jupiter.api.Test;
669
+
670
+ @Test
671
+ @DisplayName("测试添加数据 - key键为空抛出异常")
672
+ public void testAdd_ThrowsException() {
673
+ TestDemoBo bo = new TestDemoBo();
674
+ // 不设置 testKey(必填字段)
675
+
676
+ // 断言抛出 ServiceException 异常
677
+ ServiceException exception = Assertions.assertThrows(
678
+ ServiceException.class,
679
+ () -> testDemoService.insertByBo(bo)
680
+ );
681
+
682
+ // 验证异常消息
683
+ Assertions.assertTrue(exception.getMessage().contains("key键不能为空"));
684
+ }
685
+
686
+ @Test
687
+ @DisplayName("测试空指针异常")
688
+ public void testNullPointerException() {
689
+ Assertions.assertThrows(
690
+ NullPointerException.class,
691
+ () -> {
692
+ String str = null;
693
+ str.length();
694
+ }
695
+ );
696
+ }
697
+ ```
698
+
699
+ ---
700
+
701
+ ## 运行测试
702
+
703
+ ```bash
704
+ # Maven 运行所有测试
705
+ mvn test
706
+
707
+ # Maven 运行单个测试类
708
+ mvn test -Dtest=AdServiceTest
709
+
710
+ # Maven 运行单个测试方法
711
+ mvn test -Dtest=AdServiceTest#testAddAd
712
+
713
+ # IDEA 中运行
714
+ # 右键测试类/方法 → Run 'XXTest'
715
+ ```
716
+
717
+ ---
718
+
719
+ ## FAQ
720
+
721
+ ### Q1: 什么时候使用 @SpringBootTest?
722
+
723
+ **A:**
724
+ - **不使用 @SpringBootTest**:工具类、枚举、POJO(纯逻辑测试,不需要 Spring 容器)
725
+ - **使用 @SpringBootTest**:Service、Controller、Mapper(需要依赖注入和 Spring 容器)
726
+
727
+ ### Q2: Service 测试会污染数据库吗?
728
+
729
+ **A:** 不会。使用 `@SpringBootTest` + `@Transactional`,测试结束后自动回滚,不会污染数据库。
730
+
731
+ ### Q3: 如何测试私有方法?
732
+
733
+ **A:**
734
+ - 不要直接测试私有方法
735
+ - 通过公共方法间接测试
736
+ - 如果私有方法太复杂,考虑提取为独立类
737
+
738
+ ### Q4: Mock 和真实测试如何选择?
739
+
740
+ **A:**
741
+ - **单元测试**: 优先 Mock 外部依赖(快速、隔离)
742
+ - **集成测试**: 使用真实依赖(准确、完整)
743
+ - **平衡**: 既要有单元测试(快),也要有集成测试(准确)
744
+
745
+ ### Q5: @SpringBootTest 为什么找不到主类?
746
+
747
+ **A:** `@SpringBootTest` 只能在 SpringBoot 主包下使用,测试类必须与主类(包含 main 方法)在同一个包或子包下。
748
+
749
+ ### Q6: 如何跳过测试执行?
750
+
751
+ **A:**
752
+ - **单个测试**:使用 `@Disabled` 注解
753
+ - **Maven 跳过所有测试**:`mvn clean install -DskipTests`
754
+
755
+ ---