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.
- package/README.md +358 -0
- package/dist/cli/kcode.d.ts +15 -0
- package/dist/cli/kcode.js +153 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +7 -0
- package/docs/KCODE_DISTRIBUTION.md +91 -0
- package/extensions/kingdee-harness.ts +180 -0
- package/extensions/kingdee-header.ts +122 -0
- package/extensions/kingdee-tools.ts +379 -0
- package/knowledge/.backup/v1.0.0/version.json +10 -0
- package/knowledge/cangqiong/product-notes.md +15 -0
- package/knowledge/common/business-flows.md +115 -0
- package/knowledge/common/config-guides.md +110 -0
- package/knowledge/common/error-patterns.md +170 -0
- package/knowledge/common/implementation.md +144 -0
- package/knowledge/cosmic/hard-constraints.md +38 -0
- package/knowledge/cosmic/ksql-datafix.md +34 -0
- package/knowledge/cosmic/platform-baseline.md +32 -0
- package/knowledge/cosmic/plugin-decision-matrix.md +40 -0
- package/knowledge/cosmic/review-checklist.md +40 -0
- package/knowledge/cosmic/unittest.md +35 -0
- package/knowledge/enterprise/api-reference.md +186 -0
- package/knowledge/enterprise/code-patterns.md +217 -0
- package/knowledge/enterprise/plugin-lifecycle.md +188 -0
- package/knowledge/enterprise/tables.json +159 -0
- package/knowledge/flagship/api-reference.md +237 -0
- package/knowledge/flagship/code-patterns.md +246 -0
- package/knowledge/flagship/cosmic-platform-note.md +15 -0
- package/knowledge/flagship/plugin-lifecycle.md +248 -0
- package/knowledge/flagship/tables.json +159 -0
- package/knowledge/version.json +10 -0
- package/knowledge/xinghan/product-notes.md +15 -0
- package/package.json +71 -0
- package/prompts/kd-discuss.md +11 -0
- package/prompts/kd-execute.md +12 -0
- package/prompts/kd-plan.md +12 -0
- package/prompts/kd-ship.md +12 -0
- package/prompts/kd-spec.md +12 -0
- package/prompts/kd-verify.md +12 -0
- package/skills/kd-check/SKILL.md +26 -0
- package/skills/kd-cosmic-dev/SKILL.md +82 -0
- package/skills/kd-cosmic-review/SKILL.md +90 -0
- package/skills/kd-cosmic-unittest/SKILL.md +92 -0
- package/skills/kd-debug/SKILL.md +30 -0
- package/skills/kd-discuss/SKILL.md +24 -0
- package/skills/kd-execute/SKILL.md +22 -0
- package/skills/kd-gen/SKILL.md +34 -0
- package/skills/kd-ksql/SKILL.md +86 -0
- package/skills/kd-plan/SKILL.md +24 -0
- package/skills/kd-ship/SKILL.md +22 -0
- package/skills/kd-spec/SKILL.md +24 -0
- package/skills/kd-verify/SKILL.md +22 -0
- package/themes/kcode-dark.json +81 -0
- package/vendor/kingdee-skills/cosmic-unittest/SKILL.md +788 -0
- package/vendor/kingdee-skills/cosmic-unittest/author-cache.json +5 -0
- package/vendor/kingdee-skills/cosmic-unittest/cosmic-unittest-skill-overview.html +746 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/business-test.md +205 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/common-test.md +257 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/formplugin-test.md +560 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/op-plugin-test.md +231 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/validator-test.md +232 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/business-helper.md +184 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/common-module.md +355 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/convert-plugin.md +130 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/formplugin.md +235 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/op-plugin.md +226 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/validator.md +206 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/SKILL.md +674 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/advanced-scenario-checklist.md +307 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/algox-performance-checklist.md +129 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/coding-standard-checklist.md +491 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/cosmic-api-checklist.md +285 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-access-checklist.md +261 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-transaction-checklist.md +390 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/domain-logic-checklist.md +295 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/form-plugin-checklist.md +508 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/infra-checklist.md +254 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ksql-checklist.md +305 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/lifecycle-checklist.md +298 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/operation-plugin-checklist.md +442 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/test-mock-checklist.md +120 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ui-performance-checklist.md +320 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/pattern-matcher.py +336 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/review-score-calculator.py +121 -0
- package/vendor/kingdee-skills/ok-cosmic/CHANGELOG.md +295 -0
- package/vendor/kingdee-skills/ok-cosmic/README.md +460 -0
- package/vendor/kingdee-skills/ok-cosmic/SKILL.md +287 -0
- package/vendor/kingdee-skills/ok-cosmic/agents/openai.yaml +17 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/BatchImportPluginTemplate.java +93 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/BillPlugInTemplate.java +156 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ConvertPlugInTemplate.java +255 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/FormPluginTemplate.java +597 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/IWorkflowPluginTemplate.java +91 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ListPluginTemplate.java +194 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/OpPluginTemplate.java +201 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/OpenApiControllerTemplate.java +103 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/PrintPluginTemplate.java +95 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ReportFormPluginTemplate.java +257 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ReportListDataPluginTemplate.java +70 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/StandardTreeListPluginTemplate.java +130 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/TaskTemplate.java +80 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/TreeListPluginTemplate.java +152 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/WriteBackPlugInTemplate.java +286 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/attachment/AttachmentUploadBindSample.java +93 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/BotpTracePushSample.java +168 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/SampleConvertPlugin.java +223 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/cache/SampleCacheUsage.java +218 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/concurrent/SampleThreadPoolBatch.java +156 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectCrudSample.java +205 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectOpsSample.java +100 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/BeforeOperationConfirmSample.java +217 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ConfirmDialogSample.java +131 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/EntryRowCalculateSample.java +116 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/F7FilterSample.java +134 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/GetAndSetValueSample.java +176 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/HyperlinkJumpSample.java +124 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/OpenBillModalSample.java +253 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ReturnParentDataSample.java +295 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/TreeControlSample.java +140 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ViewControlOpsSample.java +132 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPluginBasicSample.java +170 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPreOpenFilterSample.java +68 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/message/MessageNotifySample.java +95 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/SampleMQConsumer.java +198 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/sample_mq.xml +15 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OpAddValidatorsSample.java +137 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OperationOptionBridgeSample.java +228 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/package-info.java +19 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BaseDataQuerySample.java +194 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BatchQuerySample.java +368 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/DataSetQueryStatSample.java +131 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportFormPlugin.java +179 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportListDataPlugin.java +616 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/snippets-guide.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/task/ScheduleTaskSample.java +160 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/workflow/SampleWorkflowPlugin.java +302 -0
- package/vendor/kingdee-skills/ok-cosmic/manifest.json +78 -0
- package/vendor/kingdee-skills/ok-cosmic/ok-cosmic-intro.html +903 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/attachment-api.md +114 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/botp-convert.md +98 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/dynamic-object.md +113 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/entity-metadata.md +123 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/event-lifecycle.md +184 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/flex-prop.md +114 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/form-utils.md +133 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/operate-chain.md +159 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/plugin-base.md +218 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/query-dataset.md +149 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/request-context.md +88 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/view-handler.md +157 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-bill.md +76 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-botp.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-form.md +165 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-import.md +69 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-list.md +227 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-openapi.md +112 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-operation.md +135 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-print.md +65 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-data.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-form.md +90 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-task.md +62 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-tree-list.md +71 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-workflow.md +82 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-writeback.md +71 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-algo.md +67 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-cache.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-model-svc.md +82 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-object.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-entity-model.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-exception.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-file.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-id.md +47 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-lock.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-log.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-network-control.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-orm-access.md +78 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-request-context.md +62 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-threadpool.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-tx.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-utils.md +67 -0
- package/vendor/kingdee-skills/ok-cosmic/requirements.txt +2 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/a-layer-rules.json +24 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/anti-patterns.md +48 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/cheat-sheet.md +256 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/coding-preferences.md +140 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/constraints.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/decision-matrix.md +222 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/intent-routing.md +94 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/platform-baseline.md +69 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/post-check.md +109 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/config_loader.py +204 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-api-knowledge.py +910 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-basedata-query.py +359 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-config-check.py +181 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-extpoints-query.py +389 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-form-metadata.py +856 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-check.py +262 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-lint.py +293 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/__init__.py +2 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/base.py +393 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/resource_check.py +176 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/scene_check.py +375 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/style_check.py +434 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/verify_check.py +36 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/route_client.py +186 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/script_utils.py +40 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/sqlite_cache.py +142 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-commons.jar +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-features.jar +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic-docs.db +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic.json +13 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup-mac.sh +18 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup-windows.bat +53 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup.jar +0 -0
- package/vendor/kingdee-skills/ok-ksql/SKILL.md +81 -0
- package/vendor/kingdee-skills/ok-ksql/agents/openai.yaml +7 -0
- package/vendor/kingdee-skills/ok-ksql/manifest.json +14 -0
- package/vendor/kingdee-skills/ok-ksql/references/ksql-datafix.md +452 -0
- 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
|
+
```
|