kcode-pi 0.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 (219) hide show
  1. package/README.md +358 -0
  2. package/dist/cli/kcode.d.ts +15 -0
  3. package/dist/cli/kcode.js +153 -0
  4. package/dist/cli/main.d.ts +2 -0
  5. package/dist/cli/main.js +7 -0
  6. package/docs/KCODE_DISTRIBUTION.md +91 -0
  7. package/extensions/kingdee-harness.ts +180 -0
  8. package/extensions/kingdee-header.ts +122 -0
  9. package/extensions/kingdee-tools.ts +379 -0
  10. package/knowledge/.backup/v1.0.0/version.json +10 -0
  11. package/knowledge/cangqiong/product-notes.md +15 -0
  12. package/knowledge/common/business-flows.md +115 -0
  13. package/knowledge/common/config-guides.md +110 -0
  14. package/knowledge/common/error-patterns.md +170 -0
  15. package/knowledge/common/implementation.md +144 -0
  16. package/knowledge/cosmic/hard-constraints.md +38 -0
  17. package/knowledge/cosmic/ksql-datafix.md +34 -0
  18. package/knowledge/cosmic/platform-baseline.md +32 -0
  19. package/knowledge/cosmic/plugin-decision-matrix.md +40 -0
  20. package/knowledge/cosmic/review-checklist.md +40 -0
  21. package/knowledge/cosmic/unittest.md +35 -0
  22. package/knowledge/enterprise/api-reference.md +186 -0
  23. package/knowledge/enterprise/code-patterns.md +217 -0
  24. package/knowledge/enterprise/plugin-lifecycle.md +188 -0
  25. package/knowledge/enterprise/tables.json +159 -0
  26. package/knowledge/flagship/api-reference.md +237 -0
  27. package/knowledge/flagship/code-patterns.md +246 -0
  28. package/knowledge/flagship/cosmic-platform-note.md +15 -0
  29. package/knowledge/flagship/plugin-lifecycle.md +248 -0
  30. package/knowledge/flagship/tables.json +159 -0
  31. package/knowledge/version.json +10 -0
  32. package/knowledge/xinghan/product-notes.md +15 -0
  33. package/package.json +71 -0
  34. package/prompts/kd-discuss.md +11 -0
  35. package/prompts/kd-execute.md +12 -0
  36. package/prompts/kd-plan.md +12 -0
  37. package/prompts/kd-ship.md +12 -0
  38. package/prompts/kd-spec.md +12 -0
  39. package/prompts/kd-verify.md +12 -0
  40. package/skills/kd-check/SKILL.md +26 -0
  41. package/skills/kd-cosmic-dev/SKILL.md +82 -0
  42. package/skills/kd-cosmic-review/SKILL.md +90 -0
  43. package/skills/kd-cosmic-unittest/SKILL.md +92 -0
  44. package/skills/kd-debug/SKILL.md +30 -0
  45. package/skills/kd-discuss/SKILL.md +24 -0
  46. package/skills/kd-execute/SKILL.md +22 -0
  47. package/skills/kd-gen/SKILL.md +34 -0
  48. package/skills/kd-ksql/SKILL.md +86 -0
  49. package/skills/kd-plan/SKILL.md +24 -0
  50. package/skills/kd-ship/SKILL.md +22 -0
  51. package/skills/kd-spec/SKILL.md +24 -0
  52. package/skills/kd-verify/SKILL.md +22 -0
  53. package/themes/kcode-dark.json +81 -0
  54. package/vendor/kingdee-skills/cosmic-unittest/SKILL.md +788 -0
  55. package/vendor/kingdee-skills/cosmic-unittest/author-cache.json +5 -0
  56. package/vendor/kingdee-skills/cosmic-unittest/cosmic-unittest-skill-overview.html +746 -0
  57. package/vendor/kingdee-skills/cosmic-unittest/examples/business-test.md +205 -0
  58. package/vendor/kingdee-skills/cosmic-unittest/examples/common-test.md +257 -0
  59. package/vendor/kingdee-skills/cosmic-unittest/examples/formplugin-test.md +560 -0
  60. package/vendor/kingdee-skills/cosmic-unittest/examples/op-plugin-test.md +231 -0
  61. package/vendor/kingdee-skills/cosmic-unittest/examples/validator-test.md +232 -0
  62. package/vendor/kingdee-skills/cosmic-unittest/patterns/business-helper.md +184 -0
  63. package/vendor/kingdee-skills/cosmic-unittest/patterns/common-module.md +355 -0
  64. package/vendor/kingdee-skills/cosmic-unittest/patterns/convert-plugin.md +130 -0
  65. package/vendor/kingdee-skills/cosmic-unittest/patterns/formplugin.md +235 -0
  66. package/vendor/kingdee-skills/cosmic-unittest/patterns/op-plugin.md +226 -0
  67. package/vendor/kingdee-skills/cosmic-unittest/patterns/validator.md +206 -0
  68. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/SKILL.md +674 -0
  69. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/advanced-scenario-checklist.md +307 -0
  70. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/algox-performance-checklist.md +129 -0
  71. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/coding-standard-checklist.md +491 -0
  72. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/cosmic-api-checklist.md +285 -0
  73. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-access-checklist.md +261 -0
  74. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-transaction-checklist.md +390 -0
  75. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/domain-logic-checklist.md +295 -0
  76. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/form-plugin-checklist.md +508 -0
  77. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/infra-checklist.md +254 -0
  78. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ksql-checklist.md +305 -0
  79. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/lifecycle-checklist.md +298 -0
  80. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/operation-plugin-checklist.md +442 -0
  81. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/test-mock-checklist.md +120 -0
  82. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ui-performance-checklist.md +320 -0
  83. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/pattern-matcher.py +336 -0
  84. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/review-score-calculator.py +121 -0
  85. package/vendor/kingdee-skills/ok-cosmic/CHANGELOG.md +295 -0
  86. package/vendor/kingdee-skills/ok-cosmic/README.md +460 -0
  87. package/vendor/kingdee-skills/ok-cosmic/SKILL.md +287 -0
  88. package/vendor/kingdee-skills/ok-cosmic/agents/openai.yaml +17 -0
  89. package/vendor/kingdee-skills/ok-cosmic/assets/BatchImportPluginTemplate.java +93 -0
  90. package/vendor/kingdee-skills/ok-cosmic/assets/BillPlugInTemplate.java +156 -0
  91. package/vendor/kingdee-skills/ok-cosmic/assets/ConvertPlugInTemplate.java +255 -0
  92. package/vendor/kingdee-skills/ok-cosmic/assets/FormPluginTemplate.java +597 -0
  93. package/vendor/kingdee-skills/ok-cosmic/assets/IWorkflowPluginTemplate.java +91 -0
  94. package/vendor/kingdee-skills/ok-cosmic/assets/ListPluginTemplate.java +194 -0
  95. package/vendor/kingdee-skills/ok-cosmic/assets/OpPluginTemplate.java +201 -0
  96. package/vendor/kingdee-skills/ok-cosmic/assets/OpenApiControllerTemplate.java +103 -0
  97. package/vendor/kingdee-skills/ok-cosmic/assets/PrintPluginTemplate.java +95 -0
  98. package/vendor/kingdee-skills/ok-cosmic/assets/ReportFormPluginTemplate.java +257 -0
  99. package/vendor/kingdee-skills/ok-cosmic/assets/ReportListDataPluginTemplate.java +70 -0
  100. package/vendor/kingdee-skills/ok-cosmic/assets/StandardTreeListPluginTemplate.java +130 -0
  101. package/vendor/kingdee-skills/ok-cosmic/assets/TaskTemplate.java +80 -0
  102. package/vendor/kingdee-skills/ok-cosmic/assets/TreeListPluginTemplate.java +152 -0
  103. package/vendor/kingdee-skills/ok-cosmic/assets/WriteBackPlugInTemplate.java +286 -0
  104. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/attachment/AttachmentUploadBindSample.java +93 -0
  105. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/BotpTracePushSample.java +168 -0
  106. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/SampleConvertPlugin.java +223 -0
  107. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/cache/SampleCacheUsage.java +218 -0
  108. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/concurrent/SampleThreadPoolBatch.java +156 -0
  109. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectCrudSample.java +205 -0
  110. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectOpsSample.java +100 -0
  111. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/BeforeOperationConfirmSample.java +217 -0
  112. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ConfirmDialogSample.java +131 -0
  113. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/EntryRowCalculateSample.java +116 -0
  114. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/F7FilterSample.java +134 -0
  115. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/GetAndSetValueSample.java +176 -0
  116. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/HyperlinkJumpSample.java +124 -0
  117. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/OpenBillModalSample.java +253 -0
  118. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ReturnParentDataSample.java +295 -0
  119. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/TreeControlSample.java +140 -0
  120. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ViewControlOpsSample.java +132 -0
  121. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPluginBasicSample.java +170 -0
  122. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPreOpenFilterSample.java +68 -0
  123. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/message/MessageNotifySample.java +95 -0
  124. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/SampleMQConsumer.java +198 -0
  125. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/sample_mq.xml +15 -0
  126. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OpAddValidatorsSample.java +137 -0
  127. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OperationOptionBridgeSample.java +228 -0
  128. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/package-info.java +19 -0
  129. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BaseDataQuerySample.java +194 -0
  130. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BatchQuerySample.java +368 -0
  131. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/DataSetQueryStatSample.java +131 -0
  132. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportFormPlugin.java +179 -0
  133. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportListDataPlugin.java +616 -0
  134. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/snippets-guide.md +64 -0
  135. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/task/ScheduleTaskSample.java +160 -0
  136. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/workflow/SampleWorkflowPlugin.java +302 -0
  137. package/vendor/kingdee-skills/ok-cosmic/manifest.json +78 -0
  138. package/vendor/kingdee-skills/ok-cosmic/ok-cosmic-intro.html +903 -0
  139. package/vendor/kingdee-skills/ok-cosmic/references/adv/attachment-api.md +114 -0
  140. package/vendor/kingdee-skills/ok-cosmic/references/adv/botp-convert.md +98 -0
  141. package/vendor/kingdee-skills/ok-cosmic/references/adv/dynamic-object.md +113 -0
  142. package/vendor/kingdee-skills/ok-cosmic/references/adv/entity-metadata.md +123 -0
  143. package/vendor/kingdee-skills/ok-cosmic/references/adv/event-lifecycle.md +184 -0
  144. package/vendor/kingdee-skills/ok-cosmic/references/adv/flex-prop.md +114 -0
  145. package/vendor/kingdee-skills/ok-cosmic/references/adv/form-utils.md +133 -0
  146. package/vendor/kingdee-skills/ok-cosmic/references/adv/operate-chain.md +159 -0
  147. package/vendor/kingdee-skills/ok-cosmic/references/adv/plugin-base.md +218 -0
  148. package/vendor/kingdee-skills/ok-cosmic/references/adv/query-dataset.md +149 -0
  149. package/vendor/kingdee-skills/ok-cosmic/references/adv/request-context.md +88 -0
  150. package/vendor/kingdee-skills/ok-cosmic/references/adv/view-handler.md +157 -0
  151. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-bill.md +76 -0
  152. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-botp.md +70 -0
  153. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-form.md +165 -0
  154. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-import.md +69 -0
  155. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-list.md +227 -0
  156. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-openapi.md +112 -0
  157. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-operation.md +135 -0
  158. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-print.md +65 -0
  159. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-data.md +64 -0
  160. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-form.md +90 -0
  161. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-task.md +62 -0
  162. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-tree-list.md +71 -0
  163. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-workflow.md +82 -0
  164. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-writeback.md +71 -0
  165. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-algo.md +67 -0
  166. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-cache.md +63 -0
  167. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-model-svc.md +82 -0
  168. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-object.md +70 -0
  169. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-entity-model.md +61 -0
  170. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-exception.md +64 -0
  171. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-file.md +63 -0
  172. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-id.md +47 -0
  173. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-lock.md +61 -0
  174. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-log.md +63 -0
  175. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-network-control.md +70 -0
  176. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-orm-access.md +78 -0
  177. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-request-context.md +62 -0
  178. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-threadpool.md +63 -0
  179. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-tx.md +64 -0
  180. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-utils.md +67 -0
  181. package/vendor/kingdee-skills/ok-cosmic/requirements.txt +2 -0
  182. package/vendor/kingdee-skills/ok-cosmic/rules/a-layer-rules.json +24 -0
  183. package/vendor/kingdee-skills/ok-cosmic/rules/anti-patterns.md +48 -0
  184. package/vendor/kingdee-skills/ok-cosmic/rules/cheat-sheet.md +256 -0
  185. package/vendor/kingdee-skills/ok-cosmic/rules/coding-preferences.md +140 -0
  186. package/vendor/kingdee-skills/ok-cosmic/rules/constraints.md +61 -0
  187. package/vendor/kingdee-skills/ok-cosmic/rules/decision-matrix.md +222 -0
  188. package/vendor/kingdee-skills/ok-cosmic/rules/intent-routing.md +94 -0
  189. package/vendor/kingdee-skills/ok-cosmic/rules/platform-baseline.md +69 -0
  190. package/vendor/kingdee-skills/ok-cosmic/rules/post-check.md +109 -0
  191. package/vendor/kingdee-skills/ok-cosmic/scripts/config_loader.py +204 -0
  192. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-api-knowledge.py +910 -0
  193. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-basedata-query.py +359 -0
  194. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-config-check.py +181 -0
  195. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-extpoints-query.py +389 -0
  196. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-form-metadata.py +856 -0
  197. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-check.py +262 -0
  198. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-lint.py +293 -0
  199. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/__init__.py +2 -0
  200. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/base.py +393 -0
  201. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/resource_check.py +176 -0
  202. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/scene_check.py +375 -0
  203. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/style_check.py +434 -0
  204. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/verify_check.py +36 -0
  205. package/vendor/kingdee-skills/ok-cosmic/scripts/route_client.py +186 -0
  206. package/vendor/kingdee-skills/ok-cosmic/scripts/script_utils.py +40 -0
  207. package/vendor/kingdee-skills/ok-cosmic/scripts/sqlite_cache.py +142 -0
  208. package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-commons.jar +0 -0
  209. package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-features.jar +0 -0
  210. package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic-docs.db +0 -0
  211. package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic.json +13 -0
  212. package/vendor/kingdee-skills/ok-cosmic/setup/setup-mac.sh +18 -0
  213. package/vendor/kingdee-skills/ok-cosmic/setup/setup-windows.bat +53 -0
  214. package/vendor/kingdee-skills/ok-cosmic/setup/setup.jar +0 -0
  215. package/vendor/kingdee-skills/ok-ksql/SKILL.md +81 -0
  216. package/vendor/kingdee-skills/ok-ksql/agents/openai.yaml +7 -0
  217. package/vendor/kingdee-skills/ok-ksql/manifest.json +14 -0
  218. package/vendor/kingdee-skills/ok-ksql/references/ksql-datafix.md +452 -0
  219. package/vendor/kingdee-skills/ok-ksql/scripts/ksql_lint.py +363 -0
@@ -0,0 +1,231 @@
1
+ # OpPlugin 操作插件测试完整示例
2
+
3
+ > 以 `ProcessPlanSaveOp` 为例,展示操作插件的完整测试写法。
4
+
5
+ ```java
6
+ package kd.mmc.sfc.opplugin.processplan;
7
+
8
+ import kd.bos.dataentity.OperateOption;
9
+ import kd.bos.dataentity.entity.DynamicObject;
10
+ import kd.bos.dataentity.entity.DynamicObjectCollection;
11
+ import kd.bos.entity.operate.result.OperationResult;
12
+ import kd.bos.entity.plugin.args.BeforeOperationArgs;
13
+ import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
14
+ import kd.bos.exception.KDBizException;
15
+ import kd.bos.form.unittest.DisplayName;
16
+ import kd.bos.org.utils.Consts;
17
+ import kd.bos.servicehelper.BusinessDataServiceHelper;
18
+ import kd.bos.test.ext.annotaions.UnittestCaseInfo;
19
+ import kd.bos.unittest.AbstractJunitNoDependenciesResManagerTest;
20
+ import kd.bos.unittest.mock.DynamicObjectMocker;
21
+ import kd.mmc.sfc.business.OperationHelper;
22
+ import kd.mmc.sfc.business.ProcessPickingHelper;
23
+ import kd.mmc.sfc.business.ProcessPlanHelper;
24
+ import kd.mmc.sfc.common.processplan.ProcessPlanConsts;
25
+ import org.junit.After;
26
+ import org.junit.Assert;
27
+ import org.junit.Before;
28
+ import org.junit.Test;
29
+ import org.mockito.MockedStatic;
30
+
31
+ import java.util.*;
32
+
33
+ import static org.mockito.ArgumentMatchers.*;
34
+ import static org.mockito.Mockito.*;
35
+
36
+ /**
37
+ * 工序计划保存操作单元测试
38
+ * @author zhang_san
39
+ * @date 2025/5/14 10:50
40
+ */
41
+ public class ProcessPlanSaveOpTest extends AbstractJunitNoDependenciesResManagerTest {
42
+
43
+ // ========== 1. 声明 MockedStatic ==========
44
+ MockedStatic<ProcessPickingHelper> processPickingHelper;
45
+ private BeginOperationTransactionArgs beginOperationTransactionArgs;
46
+
47
+ // ========== 2. @Before ==========
48
+ @Before
49
+ public void before() {
50
+ processPickingHelper = mockStatic(ProcessPickingHelper.class);
51
+
52
+ // 构建测试数据
53
+ DynamicObject[] planS = getBillS();
54
+
55
+ // Mock 事务参数
56
+ beginOperationTransactionArgs = mock(BeginOperationTransactionArgs.class);
57
+ when(beginOperationTransactionArgs.getDataEntities()).thenReturn(planS);
58
+ when(beginOperationTransactionArgs.getOperationKey())
59
+ .thenReturn(ProcessPlanConsts.OPERATION_CONST.SAVE);
60
+ }
61
+
62
+ // ========== 3. @After ==========
63
+ @After
64
+ public void after() {
65
+ processPickingHelper.close();
66
+ }
67
+
68
+ public void reset() {
69
+ processPickingHelper.reset();
70
+ }
71
+
72
+ // ========== 4. 构建测试单据 ==========
73
+ private DynamicObject[] getBillS() {
74
+ Set<DynamicObject> billSet = new HashSet<>();
75
+ billSet.add(new DynamicObjectMocker()
76
+ .add(Consts.ID, 2128998950412872704L)
77
+ .add(ProcessPlanConsts.BILLNO, "GXJH-20231109-00000047")
78
+ .add(ProcessPlanConsts.ROOTPROCESSPLAN_ID, 0L)
79
+ .add(ProcessPlanConsts.WORKROWID, 11L)
80
+ .getObject());
81
+ billSet.add(new DynamicObjectMocker()
82
+ .add(Consts.ID, 1814710693405206528L)
83
+ .add(ProcessPlanConsts.BILLNO, "GXJH-20231109-00000048")
84
+ .add(ProcessPlanConsts.ROOTPROCESSPLAN_ID, 996L)
85
+ .add(ProcessPlanConsts.WORKROWID, 22L)
86
+ .getObject());
87
+ return billSet.toArray(new DynamicObject[0]);
88
+ }
89
+
90
+ // ========== 5. 测试方法 ==========
91
+
92
+ @UnittestCaseInfo(
93
+ author = "zhang_san <zhang_san@kingdee.com>",
94
+ title = "Test begin operation transaction",
95
+ targetClass = "kd.mmc.sfc.opplugin.processplan.ProcessPlanSaveOp",
96
+ targetMethod = "beginoperationtransaction",
97
+ lastUpdateTime = "2025-05-14 14:55:40",
98
+ lastUpdateAuthor = "zhang_san <zhang_san@kingdee.com>",
99
+ methodSignature = "public void beginOperationTransaction(BeginOperationTransactionArgs e)",
100
+ testPoints = {"Functionality"},
101
+ description = "Test begin operation transaction"
102
+ )
103
+ @Test
104
+ @DisplayName("测试校验成功时")
105
+ public void testBeginOperationTransactionValidationSucceeds() {
106
+ //step 创建成功的操作结果
107
+ OperationResult operation = mock(OperationResult.class);
108
+ when(operation.isSuccess()).thenReturn(true);
109
+
110
+ // 使用匿名子类注入 Mock 的 OperationResult
111
+ ProcessPlanSaveOp saveOp = new ProcessPlanSaveOp() {
112
+ @Override
113
+ public OperationResult getOperationResult() {
114
+ return operation;
115
+ }
116
+ };
117
+ OperateOption option = OperateOption.create();
118
+ saveOp.setContext(null, null, option);
119
+
120
+ //step 执行方法
121
+ saveOp.beginOperationTransaction(beginOperationTransactionArgs);
122
+
123
+ //assert 验证 ProcessPickingHelper.setMftStockPlanNo 被调用
124
+ processPickingHelper.verify(
125
+ () -> ProcessPickingHelper.setMftStockPlanNo(any(Map.class)), times(1));
126
+ reset();
127
+ }
128
+
129
+ @Test
130
+ @DisplayName("测试校验失败时")
131
+ public void testBeginOperationTransactionValidationFails() {
132
+ //step 创建失败的操作结果
133
+ OperationResult operation = mock(OperationResult.class);
134
+ when(operation.isSuccess()).thenReturn(false);
135
+
136
+ ProcessPlanSaveOp saveOp = new ProcessPlanSaveOp() {
137
+ @Override
138
+ public OperationResult getOperationResult() {
139
+ return operation;
140
+ }
141
+ };
142
+ OperateOption option = OperateOption.create();
143
+ saveOp.setContext(null, null, option);
144
+
145
+ //step 执行方法,期望抛异常
146
+ Exception exception = Assert.assertThrows(KDBizException.class,
147
+ () -> saveOp.beginOperationTransaction(beginOperationTransactionArgs));
148
+
149
+ //assert 验证异常消息
150
+ assertTrue(exception.getMessage().contains("保存校验失败。"));
151
+ reset();
152
+ }
153
+
154
+ // ========== 6. try-with-resources 模式(方法级 Mock)==========
155
+
156
+ @Test
157
+ public void testBeforeExecuteOperationTransaction() {
158
+ // 使用 try-with-resources 管理仅此方法需要的 Mock
159
+ try (MockedStatic<OperationHelper> operationHelper = mockStatic(OperationHelper.class);
160
+ MockedStatic<ProcessPlanHelper> processPlanHelper = mockStatic(ProcessPlanHelper.class);
161
+ MockedStatic<BusinessDataServiceHelper> businessDataServiceHelper =
162
+ mockStatic(BusinessDataServiceHelper.class)) {
163
+
164
+ //step 数据构造
165
+ operationHelper.when(() -> OperationHelper.setFirstOrLastProcess(any()))
166
+ .thenAnswer(invocation -> null);
167
+
168
+ Map<String, Object> validatorResult = new HashMap<>(1);
169
+ validatorResult.put("code", 200L);
170
+ validatorResult.put("message", "errormessage");
171
+ operationHelper.when(() -> OperationHelper.validator(any()))
172
+ .thenReturn(validatorResult);
173
+
174
+ processPlanHelper.when(() -> ProcessPlanHelper.sortEntrySetRowSeq(any()))
175
+ .thenAnswer(invocation -> null);
176
+
177
+ businessDataServiceHelper.when(() ->
178
+ BusinessDataServiceHelper.loadFromCache(anyString(), anyString(), any()))
179
+ .thenReturn(new HashMap<>(0));
180
+
181
+ // 构建带分录的复杂单据
182
+ DynamicObject entry = new DynamicObjectMocker()
183
+ .add(ProcessPlanConsts.ENTRY.PROCESSSEQUENCE, 1)
184
+ .add(ProcessPlanConsts.ENTRY.PROCESSNUMBER, 10)
185
+ .add("processbarcode", null)
186
+ .add("entryentity_lk", new DynamicObjectMocker()
187
+ .add("entryentity_lk_stableid", "12345")
188
+ .add("entryentity_lk_sid", "147258")
189
+ .add("entryentity_lk_sbillid", "456789").getCollection())
190
+ .getObject();
191
+
192
+ DynamicObjectCollection entries = new DynamicObjectCollection();
193
+ entries.add(entry);
194
+
195
+ DynamicObject bill = new DynamicObjectMocker()
196
+ .add(ProcessPlanConsts.BILLNO, "GXJH-20251208-00000001")
197
+ .add(ProcessPlanConsts.ENTRY.ENTRYENTITY_ENTITY, entries)
198
+ .add(ProcessPlanConsts.ROOTPROCESSPLAN_ID, 1L)
199
+ .add(ProcessPlanConsts.SPLITLEVEL, 0)
200
+ .getObject();
201
+
202
+ BeforeOperationArgs args = mock(BeforeOperationArgs.class);
203
+ when(args.getDataEntities()).thenReturn(new DynamicObject[]{bill});
204
+ when(args.getOperationKey()).thenReturn("save");
205
+
206
+ //step 执行方法
207
+ ProcessPlanSaveOp saveOp = new ProcessPlanSaveOp();
208
+ saveOp.beforeExecuteOperationTransaction(args);
209
+
210
+ //assert 验证条码已生成
211
+ String barcode = bill.getDynamicObjectCollection(
212
+ ProcessPlanConsts.ENTRY.ENTRYENTITY_ENTITY)
213
+ .get(0).getString("processbarcode");
214
+ assertTrue(barcode != null && !barcode.isEmpty());
215
+ reset();
216
+ }
217
+ // MockedStatic 自动关闭
218
+ }
219
+ }
220
+ ```
221
+
222
+ ## 要点总结
223
+
224
+ 1. **匿名子类注入 OperationResult**:操作插件依赖 `getOperationResult()` 判断成败,
225
+ 通过匿名子类 override 注入 Mock 对象
226
+ 2. **setContext**:操作插件需要调用 `setContext(entityType, operateMeta, option)` 初始化上下文
227
+ 3. **Mock Args**:`BeginOperationTransactionArgs` 等参数对象用 `mock()` 创建,
228
+ 配置 `getDataEntities()` 和 `getOperationKey()` 返回值
229
+ 4. **try-with-resources**:仅在单个测试方法中使用的 Mock 可用此模式自动管理生命周期
230
+ 5. **assertThrows**:测试异常场景使用 `Assert.assertThrows(ExceptionClass.class, () -> ...)`
231
+ 6. **verify 验证调用**:用 `mockStatic.verify(() -> method(), times(n))` 验证静态方法被调用次数
@@ -0,0 +1,232 @@
1
+ # Validator 校验器测试完整示例
2
+
3
+ > 以 `OutSendProcessQtyValidator` 为例,展示校验器的完整测试写法。
4
+
5
+ ```java
6
+ package kd.mmc.sfc.opplugin.validator;
7
+
8
+ import kd.bos.dataentity.entity.DynamicObject;
9
+ import kd.bos.entity.ExtendedDataEntity;
10
+ import kd.bos.entity.validate.ErrorLevel;
11
+ import kd.bos.test.ext.annotaions.UnittestCaseInfo;
12
+ import kd.bos.unittest.AbstractJunitNoDependenciesResManagerTest;
13
+ import kd.bos.unittest.mock.DynamicObjectMocker;
14
+ import kd.mmc.sfc.business.OutSourceSendQtyCheckHelper;
15
+ import kd.mmc.sfc.business.OutsourceSendHelper;
16
+ import kd.mmc.sfc.business.ProcessPlanHelper;
17
+ import kd.mmc.sfc.common.insideaccept.InsideAcceptConst;
18
+ import kd.mmc.sfc.common.outsourcesend.OutsourceSendConsts;
19
+ import kd.mmc.sfc.opplugin.Validator.OutSendProcessQtyValidator;
20
+ import org.junit.After;
21
+ import org.junit.Before;
22
+ import org.junit.Test;
23
+ import org.mockito.MockedStatic;
24
+ import org.mockito.Mockito;
25
+ import org.mockito.MockitoAnnotations;
26
+
27
+ import java.math.BigDecimal;
28
+ import java.util.*;
29
+
30
+ import static org.mockito.ArgumentMatchers.*;
31
+ import static org.mockito.Mockito.*;
32
+
33
+ public class OutSendProcessQtyValidatorTest extends AbstractJunitNoDependenciesResManagerTest {
34
+
35
+ // ========== 1. Mock 声明 ==========
36
+ private MockedStatic<OutsourceSendHelper> outsourceSendHelper;
37
+ private MockedStatic<ProcessPlanHelper> processPlanHelper;
38
+ private MockedStatic<OutSourceSendQtyCheckHelper> outSourceSendQtyCheckHelper;
39
+
40
+ // ========== 2. 错误收集器 ==========
41
+ private List<Integer> errorIndexs = new ArrayList<>();
42
+ private final List<String> errorMessages = new ArrayList<>();
43
+
44
+ @Before
45
+ public void before() {
46
+ MockitoAnnotations.openMocks(this);
47
+ outsourceSendHelper = Mockito.mockStatic(OutsourceSendHelper.class);
48
+ processPlanHelper = Mockito.mockStatic(ProcessPlanHelper.class);
49
+ outSourceSendQtyCheckHelper = Mockito.mockStatic(OutSourceSendQtyCheckHelper.class);
50
+ }
51
+
52
+ @After
53
+ public void after() {
54
+ outsourceSendHelper.close();
55
+ processPlanHelper.close();
56
+ outSourceSendQtyCheckHelper.close();
57
+ }
58
+
59
+ // ========== 3. 构建校验器(核心:匿名子类重写消息方法)==========
60
+ private OutSendProcessQtyValidator buildValidator() {
61
+ return new OutSendProcessQtyValidator() {
62
+ @Override
63
+ protected void addMessage(ExtendedDataEntity dataEntity, String content) {
64
+ errorIndexs.add(dataEntity.getDataEntityIndex());
65
+ errorMessages.add(content);
66
+ }
67
+ @Override
68
+ protected void addMessage(ExtendedDataEntity dataEntity, String content, ErrorLevel errorLevel) {
69
+ errorIndexs.add(dataEntity.getDataEntityIndex());
70
+ errorMessages.add(content);
71
+ }
72
+ @Override
73
+ protected void addErrorMessage(ExtendedDataEntity dataEntity, String content) {
74
+ errorIndexs.add(dataEntity.getDataEntityIndex());
75
+ errorMessages.add(content);
76
+ }
77
+ @Override
78
+ protected void addWarningMessage(ExtendedDataEntity dataEntity, String content) {
79
+ errorIndexs.add(dataEntity.getDataEntityIndex());
80
+ errorMessages.add(content);
81
+ }
82
+ };
83
+ }
84
+
85
+ // ========== 4. 构建测试数据(DynamicObjectMocker 复杂嵌套)==========
86
+ private ExtendedDataEntity[] buildValidatorData() {
87
+ // 单据一:数量超出(应触发校验错误)
88
+ DynamicObject billOne = new DynamicObjectMocker()
89
+ .add("id", 123456L)
90
+ .add(OutsourceSendConsts.BILLTYPEID_ID, 12334L)
91
+ .add(OutsourceSendConsts.BILLSTATUS, "A")
92
+ .add("entryentity", new DynamicObjectMocker()
93
+ .add("id", 1L)
94
+ .add("seq", 1)
95
+ .add("proplanentry_id", 2L)
96
+ .add("proplanentry", new DynamicObjectMocker()
97
+ .add("id", 2L)
98
+ .add("headunitfactor", 1)
99
+ .add("prounitfactor", 1)
100
+ .add("processsequence", 1)
101
+ .add("processnumber", 10).getObject())
102
+ .add("proplan_id", 3L)
103
+ .add("material", new DynamicObjectMocker()
104
+ .add("masterid.id", 4L).getObject())
105
+ .add("processqty", new BigDecimal(11)) // 超出 cansendqty
106
+ .add("cansendqty", new BigDecimal(10))
107
+ .add("unit", new DynamicObjectMocker()
108
+ .add("id", 1L)
109
+ .add("precision", 1).getObject())
110
+ .getCollection())
111
+ .getObject();
112
+
113
+ // 单据二:数量正常(校验通过)
114
+ DynamicObject billTwo = new DynamicObjectMocker()
115
+ .add("id", 123456L)
116
+ .add(OutsourceSendConsts.BILLTYPEID_ID, 12334L)
117
+ .add(OutsourceSendConsts.BILLSTATUS, "A")
118
+ .add("entryentity", new DynamicObjectMocker()
119
+ .add("id", 1L)
120
+ .add("seq", 1)
121
+ .add("proplanentry_id", 2000000L)
122
+ .add("proplanentry", new DynamicObjectMocker()
123
+ .add("id", 2000000L)
124
+ .add("headunitfactor", 1)
125
+ .add("prounitfactor", 1)
126
+ .add("processsequence", 1)
127
+ .add("processnumber", 20).getObject())
128
+ .add("proplan_id", 30000000L)
129
+ .add("material", new DynamicObjectMocker()
130
+ .add("masterid.id", 4L).getObject())
131
+ .add("processqty", BigDecimal.TEN) // 等于 cansendqty
132
+ .add("cansendqty", BigDecimal.TEN)
133
+ .add("unit", new DynamicObjectMocker()
134
+ .add("id", 1L)
135
+ .add("precision", 1).getObject())
136
+ .getCollection())
137
+ .getObject();
138
+
139
+ return new ExtendedDataEntity[]{
140
+ new ExtendedDataEntity(billOne, 0, 0),
141
+ new ExtendedDataEntity(billTwo, 0, 0)
142
+ };
143
+ }
144
+
145
+ // ========== 5. 测试方法 ==========
146
+
147
+ @UnittestCaseInfo(
148
+ author = "zhang_san <zhang_san@kingdee.com>",
149
+ title = "Test validate should not generate error",
150
+ targetClass = "kd.mmc.sfc.opplugin.validator.OutSendProcessQtyValidator",
151
+ targetMethod = "validate",
152
+ lastUpdateTime = "2025-08-05 19:54:35",
153
+ lastUpdateAuthor = "zhang_san <zhang_san@kingdee.com>",
154
+ methodSignature = "null",
155
+ testPoints = {"Functionality"},
156
+ description = "Test validate should not generate error"
157
+ )
158
+ @Test
159
+ public void testValidate_ShouldNotGenerateError() {
160
+ //step 配置 Mock(所有外部查询返回空/默认值)
161
+ processPlanHelper.when(() -> ProcessPlanHelper.getPlanInfo(any(Set.class)))
162
+ .thenReturn(new HashMap<>());
163
+ outsourceSendHelper.when(() -> OutsourceSendHelper.getOutSendInfo(any(Set.class)))
164
+ .thenReturn(new HashMap<>());
165
+ outsourceSendHelper.when(() -> OutsourceSendHelper.getRelatedProcess(any()))
166
+ .thenReturn(new ArrayList<>());
167
+
168
+ //step 构建校验器和数据
169
+ ExtendedDataEntity[] dataEntities = buildValidatorData();
170
+ OutSendProcessQtyValidator validator = buildValidator();
171
+ validator.setEntityKey(InsideAcceptConst.FORMID);
172
+ validator.setDataEntities(dataEntities);
173
+
174
+ //step 执行校验
175
+ validator.validate();
176
+
177
+ //assert 无错误
178
+ assertEquals(0, errorIndexs.size());
179
+ }
180
+
181
+ @UnittestCaseInfo(
182
+ author = "zhang_san <zhang_san@kingdee.com>",
183
+ title = "Test validate should generate error",
184
+ targetClass = "kd.mmc.sfc.opplugin.validator.OutSendProcessQtyValidator",
185
+ targetMethod = "validate",
186
+ lastUpdateTime = "2025-08-05 19:54:35",
187
+ lastUpdateAuthor = "zhang_san <zhang_san@kingdee.com>",
188
+ methodSignature = "null",
189
+ testPoints = {"Functionality"},
190
+ description = "Test validate should generate error"
191
+ )
192
+ @Test
193
+ public void testValidate_ShouldGenerateError() {
194
+ //step 配置 Mock(构造会触发校验失败的数据环境)
195
+ Map<Long, Map<String, Object>> planInfos = new HashMap<>();
196
+ Map<String, Object> map = new HashMap<>(16);
197
+ map.put("id", 1L);
198
+ map.put("billno", "GXJH-20231207-00000167");
199
+ map.put("entryentity.higherqty", BigDecimal.ZERO);
200
+ map.put("entryentity.outsourcedsbqty", BigDecimal.ZERO);
201
+ map.put("entryentity.outsourcedqty", BigDecimal.ZERO);
202
+ map.put("entryentity.reportordercontrol", "B");
203
+ planInfos.put(2L, map);
204
+
205
+ processPlanHelper.when(() -> ProcessPlanHelper.getPlanInfo(any(Set.class)))
206
+ .thenReturn(planInfos);
207
+ outsourceSendHelper.when(() -> OutsourceSendHelper.getOutSendInfo(any(Set.class)))
208
+ .thenReturn(new HashMap<>());
209
+ outsourceSendHelper.when(() -> OutsourceSendHelper.getRelatedProcess(any()))
210
+ .thenReturn(new ArrayList<>());
211
+
212
+ //step 执行校验
213
+ ExtendedDataEntity[] dataEntities = buildValidatorData();
214
+ OutSendProcessQtyValidator validator = buildValidator();
215
+ validator.setEntityKey(InsideAcceptConst.FORMID);
216
+ validator.setDataEntities(dataEntities);
217
+ validator.validate();
218
+
219
+ //assert 应有 2 条错误
220
+ assertEquals(2, errorIndexs.size());
221
+ }
222
+ }
223
+ ```
224
+
225
+ ## 要点总结
226
+
227
+ 1. **匿名子类重写** `addMessage` / `addErrorMessage` / `addWarningMessage` 收集错误到 List
228
+ 2. **ExtendedDataEntity 包装**:`new ExtendedDataEntity(dynamicObject, rowIndex, 0)`
229
+ 3. **setEntityKey** 设置单据标识(部分校验器依赖此值判断逻辑分支)
230
+ 4. **setDataEntities** 设置校验数据后再调用 `validate()`
231
+ 5. **断言错误数量**:`assertEquals(expectedCount, errorMessages.size())`
232
+ 6. 构造**通过和不通过两组数据**,分别验证正常和异常路径
@@ -0,0 +1,184 @@
1
+ # Business Helper 测试模式
2
+
3
+ ## 适用范围
4
+
5
+ `<模块组>-business` 工程中的静态帮助类,如 `XxxHelper`、`XxxService` 等。
6
+ 这些类通常包含大量 `public static` 方法,内部调用各种 ServiceHelper 静态方法。
7
+
8
+ ## 测试文件位置
9
+
10
+ ```
11
+ <模块组>-business/src/test/java/kd/<包路径>/business/XxxHelperTest.java
12
+ ```
13
+
14
+ 与被测类保持相同的包路径。
15
+
16
+ ## 基类选择
17
+
18
+ | 场景 | 基类 |
19
+ |------|------|
20
+ | 不需要 ResManager | `AbstractJunitNoDependenciesTest` |
21
+ | 需要 ResManager(多语言) | `AbstractJunitNoDependenciesResManagerTest` |
22
+ | 需要通用 Mock 集合 | 继承项目内 `BaseTest`(已封装常用 Mock) |
23
+
24
+ ## 模板结构
25
+
26
+ ```java
27
+ package kd.<包路径>.business;
28
+
29
+ import kd.bos.dataentity.entity.DynamicObject;
30
+ import kd.bos.form.unittest.DisplayName;
31
+ import kd.bos.servicehelper.QueryServiceHelper;
32
+ import kd.bos.servicehelper.BusinessDataServiceHelper;
33
+ import kd.bos.test.ext.annotaions.UnittestCaseInfo;
34
+ import kd.bos.unittest.AbstractJunitNoDependenciesResManagerTest;
35
+ import kd.bos.unittest.mock.DynamicObjectMocker;
36
+ import kd.bos.unittest.mock.ReflectHelper;
37
+ import org.junit.After;
38
+ import org.junit.Before;
39
+ import org.junit.Test;
40
+ import org.mockito.MockedStatic;
41
+ import org.mockito.Mockito;
42
+
43
+ import static org.mockito.ArgumentMatchers.*;
44
+ import static org.mockito.Mockito.mockStatic;
45
+
46
+ /**
47
+ * XxxHelper 单元测试
48
+ * @author 作者
49
+ * @version 1.0
50
+ * @date YYYY/MM/DD
51
+ */
52
+ public class XxxHelperTest extends AbstractJunitNoDependenciesResManagerTest {
53
+
54
+ // 1. 声明需要 Mock 的静态类
55
+ MockedStatic<QueryServiceHelper> queryServiceHelper;
56
+ MockedStatic<BusinessDataServiceHelper> businessDataServiceHelper;
57
+ // 根据被测方法实际依赖添加更多...
58
+
59
+ @Before
60
+ public void before() {
61
+ // 2. 初始化 MockedStatic
62
+ queryServiceHelper = mockStatic(QueryServiceHelper.class);
63
+ businessDataServiceHelper = mockStatic(BusinessDataServiceHelper.class);
64
+ }
65
+
66
+ @After
67
+ public void after() {
68
+ // 3. 关闭所有 MockedStatic(必须!)
69
+ queryServiceHelper.close();
70
+ businessDataServiceHelper.close();
71
+ }
72
+
73
+ public void reset() {
74
+ // 4. 可选:在每个测试方法末尾重置状态
75
+ queryServiceHelper.reset();
76
+ businessDataServiceHelper.reset();
77
+ }
78
+
79
+ // ========== 测试方法 ==========
80
+
81
+ @UnittestCaseInfo(
82
+ author = "<name> <<name>@kingdee.com>",
83
+ title = "Test method description",
84
+ targetClass = "kd.<包路径>.business.XxxHelper",
85
+ targetMethod = "methodname",
86
+ lastUpdateTime = "yyyy-MM-dd HH:mm:ss",
87
+ lastUpdateAuthor = "<name> <<name>@kingdee.com>",
88
+ methodSignature = "public static ReturnType methodName(ParamType param)",
89
+ testPoints = {"Functionality"},
90
+ description = "Test method description"
91
+ )
92
+ @Test
93
+ @DisplayName("测试场景:正常路径")
94
+ public void testMethodName_NormalCase() {
95
+ //step 构建 Mock 返回数据
96
+ queryServiceHelper.when(() -> QueryServiceHelper.query(anyString(), anyString(), any()))
97
+ .thenReturn(new DynamicObjectMocker()
98
+ .add("id", 1L)
99
+ .add("billno", "TEST-001")
100
+ .getCollection());
101
+
102
+ //step 执行被测方法
103
+ Object result = XxxHelper.targetMethod(param);
104
+
105
+ //assert 断言结果
106
+ assertNotNull(result);
107
+ assertEquals(expectedValue, result);
108
+ reset();
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## 关键技巧
114
+
115
+ ### 1. 测试 protected 方法
116
+
117
+ ```java
118
+ @Test
119
+ public void testProtectedMethod() {
120
+ XxxHelper instance = new XxxHelper();
121
+ Object result = ReflectHelper.invokeProtectedMethod(
122
+ XxxHelper.class, instance, "protectedMethodName", param1, param2);
123
+ assertEquals(expected, result);
124
+ }
125
+ ```
126
+
127
+ ### 2. 测试 private static 方法
128
+
129
+ ```java
130
+ @Test
131
+ public void testPrivateStaticMethod() {
132
+ ReflectHelper.invokeStaticMethod(
133
+ XxxHelper.class, "privateStaticMethodName", param1, param2);
134
+ // 通过 verify 或 side-effect 断言
135
+ }
136
+ ```
137
+
138
+ ### 3. 使用项目 BaseTest 基类
139
+
140
+ 如果被测方法依赖多个常见服务(OrgUnitServiceHelper、BillTypeParamHelper 等),
141
+ 直接继承 `BaseTest`(在 `<模块组>-business/src/test/java/` 下查找)可省去重复的 Mock 初始化代码。BaseTest 已预置:
142
+
143
+ - OrgUnitServiceHelper
144
+ - BillTypeParamHelper
145
+ - ResManager(thenAnswer 返回第一个参数)
146
+ - SystemParamServiceHelper
147
+ - QueryServiceHelper
148
+ - BusinessDataServiceHelper
149
+ - OperationServiceHelper
150
+ - BaseDataServiceHelper
151
+ - SaveServiceHelper
152
+
153
+ ### 4. CommonMockObject 工具
154
+
155
+ > 此为通用工具,详见 SKILL.md 2.9 节。此处展示 business 工程的典型用法。
156
+
157
+ ```java
158
+ // 获取 Mock 的 IDataModel 和 IFormView
159
+ IDataModel model = CommonMockObject.getTestModel();
160
+ IFormView view = CommonMockObject.getTestView();
161
+
162
+ // 从 JSON 字符串构造 DynamicObject
163
+ DynamicObject obj = CommonMockObject.getDynByString("{\"id\":1,\"name\":\"test\"}");
164
+
165
+ // 从 Map 构造 DynamicObjectMocker(支持嵌套)
166
+ DynamicObjectMocker mocker = CommonMockObject.getDynMockerByMap(map);
167
+ ```
168
+
169
+ ### 5. Mock 静态方法的 thenAnswer 模式
170
+
171
+ > 此为通用技巧,详见 SKILL.md 2.10 节。此处展示 business 工程的典型用法。
172
+
173
+ 当需要根据入参动态返回时:
174
+
175
+ ```java
176
+ queryServiceHelper.when(() -> QueryServiceHelper.query(anyString(), anyString(), any()))
177
+ .thenAnswer(invocation -> {
178
+ String entityName = invocation.getArgument(0);
179
+ if ("mmc_processplan".equals(entityName)) {
180
+ return mockProcessPlanData();
181
+ }
182
+ return new DynamicObjectCollection();
183
+ });
184
+ ```