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,560 @@
|
|
|
1
|
+
# FormPlugin 测试完整示例
|
|
2
|
+
|
|
3
|
+
> 以 `KeyPartCollectPluginTest` 为例,展示 mmc-sfc-formplugin 工程中继承 `BasePluginTest<T>` 的表单插件测试写法。
|
|
4
|
+
> 来源: `kd.mmc.sfc.formplugin.keypart.KeyPartCollectPluginTest`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. 类声明 & 泛型
|
|
9
|
+
|
|
10
|
+
```java
|
|
11
|
+
package kd.mmc.sfc.formplugin.keypart;
|
|
12
|
+
|
|
13
|
+
// 核心 import(省略其余)
|
|
14
|
+
import kd.mmc.sfc.formplugin.BasePluginTest;
|
|
15
|
+
import kd.bos.test.ext.annotaions.UnittestCaseInfo;
|
|
16
|
+
import kd.bos.unittest.mock.DynamicObjectMocker;
|
|
17
|
+
import kd.bos.unittest.mock.ReflectHelper;
|
|
18
|
+
import org.junit.After;
|
|
19
|
+
import org.junit.Before;
|
|
20
|
+
import org.junit.Test;
|
|
21
|
+
import org.mockito.MockedStatic;
|
|
22
|
+
import org.mockito.Mockito;
|
|
23
|
+
|
|
24
|
+
import static org.mockito.ArgumentMatchers.*;
|
|
25
|
+
import static org.mockito.Mockito.*;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 关键件采集插件测试
|
|
29
|
+
* 继承 BasePluginTest 并传入待测试插件类型作为泛型参数
|
|
30
|
+
*/
|
|
31
|
+
public class KeyPartCollectPluginTest extends BasePluginTest<KeyPartCollectPlugin> {
|
|
32
|
+
|
|
33
|
+
// ========== 1. 声明多个方法共用的 MockedStatic 变量 ==========
|
|
34
|
+
protected MockedStatic<KeypartCollectHelper> keyPartCollectHelper;
|
|
35
|
+
private MockedStatic<Uuid16> uuid16;
|
|
36
|
+
private MockedStatic<FormMetadataCache> formMetadataCache;
|
|
37
|
+
private MockedStatic<ObjectConverter> objectConverter;
|
|
38
|
+
|
|
39
|
+
// ========== 2. @Before 初始化所有 MockedStatic ==========
|
|
40
|
+
@Before
|
|
41
|
+
public void setUp() {
|
|
42
|
+
keyPartCollectHelper = Mockito.mockStatic(KeypartCollectHelper.class);
|
|
43
|
+
|
|
44
|
+
Uuid16 mockUuid = mock(Uuid16.class);
|
|
45
|
+
when(mockUuid.toString()).thenReturn("testUuid");
|
|
46
|
+
uuid16 = Mockito.mockStatic(Uuid16.class);
|
|
47
|
+
uuid16.when(Uuid16::create).thenReturn(mockUuid);
|
|
48
|
+
|
|
49
|
+
formMetadataCache = Mockito.mockStatic(FormMetadataCache.class);
|
|
50
|
+
FormConfig formConfig = mock(FormConfig.class);
|
|
51
|
+
when(formConfig.getListFormId()).thenReturn("bos_list");
|
|
52
|
+
when(formConfig.getF7ListFormId()).thenReturn("bos_listf7");
|
|
53
|
+
formMetadataCache.when(() -> FormMetadataCache.getListFormConfig(anyString()))
|
|
54
|
+
.thenReturn(formConfig);
|
|
55
|
+
|
|
56
|
+
objectConverter = Mockito.mockStatic(ObjectConverter.class, Mockito.CALLS_REAL_METHODS);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ========== 3. reset 方法(可选,用于同一用例中多次调用需要重置 mock 的场景) ==========
|
|
60
|
+
public void reset() {
|
|
61
|
+
keyPartCollectHelper.reset();
|
|
62
|
+
uuid16.reset();
|
|
63
|
+
formMetadataCache.reset();
|
|
64
|
+
objectConverter.reset();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ========== 4. @After 释放所有 MockedStatic ==========
|
|
68
|
+
@After
|
|
69
|
+
public void tearDown() {
|
|
70
|
+
keyPartCollectHelper.close();
|
|
71
|
+
uuid16.close();
|
|
72
|
+
formMetadataCache.close();
|
|
73
|
+
objectConverter.close();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ========== 5. 必须实现:createPlugin 工厂方法 ==========
|
|
77
|
+
@Override
|
|
78
|
+
protected KeyPartCollectPlugin createPlugin() {
|
|
79
|
+
return new KeyPartCollectPlugin();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ... 测试方法及辅助方法(下方分节展示)
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**要点**:
|
|
87
|
+
- 继承 `BasePluginTest<T>` 并指定被测插件类型
|
|
88
|
+
- 必须实现 `createPlugin()` 返回插件实例
|
|
89
|
+
- BasePluginTest 已提供:`testView`、`testModel`、`plugin`、`pageCache` 等常用 mock
|
|
90
|
+
- 多方法共用的 MockedStatic 在 `@Before` 初始化,`@After` 关闭
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 2. 辅助 Mock 方法(构造测试数据)
|
|
95
|
+
|
|
96
|
+
```java
|
|
97
|
+
/**
|
|
98
|
+
* 构造"待采集产品序列号"分录数据
|
|
99
|
+
* 展示:DynamicObjectMocker 构造 + when(testModel.getEntryEntity(...)) 绑定
|
|
100
|
+
*/
|
|
101
|
+
private DynamicObjectCollection mockModelGetProdSnEntry(int size) {
|
|
102
|
+
DynamicObjectCollection mockProdSn = new DynamicObjectCollection();
|
|
103
|
+
when(testModel.getEntryEntity(eq(ENTRYENTITY_PRODSN.ENTRYENTITY))).thenReturn(mockProdSn);
|
|
104
|
+
if (size <= 0) {
|
|
105
|
+
return mockProdSn;
|
|
106
|
+
}
|
|
107
|
+
for (int i = 0; i < size; i++) {
|
|
108
|
+
long id = i + 1;
|
|
109
|
+
DynamicObject e = new DynamicObjectMocker()
|
|
110
|
+
.add(ENTRYENTITY_PRODSN.UUID, "testuuid")
|
|
111
|
+
.add(ENTRYENTITY_PRODSN.PROPLANENTRY_ID, id)
|
|
112
|
+
.add(ENTRYENTITY_PRODSN.WORKROW_ID, id)
|
|
113
|
+
.add(ENTRYENTITY_PRODSN.PROCESSDEPART_ID, id)
|
|
114
|
+
.getObject();
|
|
115
|
+
mockProdSn.add(e);
|
|
116
|
+
}
|
|
117
|
+
return mockProdSn;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 构造含子单据体的"关键件分录"数据
|
|
122
|
+
* 展示:嵌套 DynamicObjectCollection 的构造方式
|
|
123
|
+
*/
|
|
124
|
+
private DynamicObjectCollection mockModelGetKeyPartEntry(int entrySize, int subEntrySize) {
|
|
125
|
+
DynamicObjectCollection keyPartColl = new DynamicObjectCollection();
|
|
126
|
+
when(testModel.getEntryEntity(eq(ENTRYENTITY_KEYPART.ENTRYENTITY))).thenReturn(keyPartColl);
|
|
127
|
+
if (entrySize <= 0) {
|
|
128
|
+
return keyPartColl;
|
|
129
|
+
}
|
|
130
|
+
DynamicObjectCollection subEntry = new DynamicObjectCollection();
|
|
131
|
+
for (int i = 0; i < entrySize; i++) {
|
|
132
|
+
long id = i + 1;
|
|
133
|
+
DynamicObject e = new DynamicObjectMocker()
|
|
134
|
+
.add(ENTRYENTITY_KEYPART.MATERIALMASTER_ID, id)
|
|
135
|
+
.add(ENTRYENTITY_KEYPART.MFTSTOCKENTRYID, id)
|
|
136
|
+
.add(ENTRYENTITY_KEYPART.MFTSTOCKENTRYID_ID, id)
|
|
137
|
+
.add(ENTRYENTITY_KEYPART.CHILDMATUNIT,
|
|
138
|
+
new DynamicObjectMocker().add(Consts.ID, 1L).getObject())
|
|
139
|
+
.add(SUBENTRYENTITY_COLL.ENTITY, subEntry)
|
|
140
|
+
.getObject();
|
|
141
|
+
keyPartColl.add(e);
|
|
142
|
+
for (int j = 0; j < subEntrySize; j++) {
|
|
143
|
+
subEntry.add(new DynamicObjectMocker()
|
|
144
|
+
.add(SUBENTRYENTITY_COLL.COLLECTQTY, BigDecimal.ZERO)
|
|
145
|
+
.add(SUBENTRYENTITY_COLL.CHILDMATUNIT_COLLDET_ID, 1L)
|
|
146
|
+
.add(SUBENTRYENTITY_COLL.KEYSN, "testsn")
|
|
147
|
+
.add(SUBENTRYENTITY_COLL.KEYBATCHNO, "testbn")
|
|
148
|
+
.add(SUBENTRYENTITY_COLL.KEYSUPPLIER_ID, 1L)
|
|
149
|
+
.getObject());
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return keyPartColl;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**要点**:
|
|
157
|
+
- 使用 `DynamicObjectMocker().add(key, value).getObject()` 构造 DynamicObject
|
|
158
|
+
- 常量引用来自业务常量类(如 `ENTRYENTITY_PRODSN.UUID`),禁止使用字面值字符串
|
|
159
|
+
- 嵌套 DynamicObject 通过再次 `new DynamicObjectMocker()` 构建
|
|
160
|
+
- 辅助方法抽取共用的 mock 逻辑,减少各测试方法的重复代码
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 3. Switch 分支测试(beforeDoOperation)
|
|
165
|
+
|
|
166
|
+
每个 switch 操作码对应独立测试方法:
|
|
167
|
+
|
|
168
|
+
```java
|
|
169
|
+
@UnittestCaseInfo(
|
|
170
|
+
author = "zhang_san <zhang_san@kingdee.com>",
|
|
171
|
+
title = "Test before do operation open record",
|
|
172
|
+
targetClass = "kd.mmc.sfc.formplugin.keypart.KeyPartCollectPlugin",
|
|
173
|
+
targetMethod = "testBeforeDoOperation",
|
|
174
|
+
lastUpdateTime = "2025-12-01 16:17:26",
|
|
175
|
+
lastUpdateAuthor = "zhang_san <zhang_san@kingdee.com>",
|
|
176
|
+
methodSignature = "public void beforeDoOperation(BeforeDoOperationEventArgs args)",
|
|
177
|
+
testPoints = {"Functionality"},
|
|
178
|
+
description = "Test before do operation open record"
|
|
179
|
+
)
|
|
180
|
+
@Test
|
|
181
|
+
public void testBeforeDoOperation_openRecord() {
|
|
182
|
+
//step 点击"采集记录"
|
|
183
|
+
BeforeDoOperationEventArgs recordsetting =
|
|
184
|
+
mockBeforeDoOperationEventArgs(OP.OPENRECORD);
|
|
185
|
+
plugin.beforeDoOperation(recordsetting);
|
|
186
|
+
//assert 打开采集记录列表
|
|
187
|
+
verify(testView).showForm(any());
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@UnittestCaseInfo(/* ... */)
|
|
191
|
+
@Test
|
|
192
|
+
public void testBeforeDoOperation_openSnSel() {
|
|
193
|
+
//step 采集主体=工序产品序列号
|
|
194
|
+
this.mockModelGetCollectSubject(CollectSubjectEnum.PROPLAN_SN);
|
|
195
|
+
BeforeDoOperationEventArgs recordsetting =
|
|
196
|
+
mockBeforeDoOperationEventArgs(OP.SELPROCESSSN);
|
|
197
|
+
plugin.beforeDoOperation(recordsetting);
|
|
198
|
+
//assert 执行选择弹窗
|
|
199
|
+
verify(testView).showForm(any());
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
@UnittestCaseInfo(/* ... */)
|
|
203
|
+
@Test
|
|
204
|
+
public void testBeforeDoOperation_openProPlanSel() {
|
|
205
|
+
//step 采集主体=工序计划,点击选择弹窗,待采集产品序列号分录为空
|
|
206
|
+
this.mockModelGetCollectSubject(CollectSubjectEnum.PROPLAN);
|
|
207
|
+
this.mockModelGetProdSnEntry(0);
|
|
208
|
+
//step
|
|
209
|
+
BeforeDoOperationEventArgs recordsetting =
|
|
210
|
+
mockBeforeDoOperationEventArgs(OP.SELPROPLAN);
|
|
211
|
+
plugin.beforeDoOperation(recordsetting);
|
|
212
|
+
//assert 执行选择弹窗,未排除当前已存在工序计划分录
|
|
213
|
+
verify(testView).showForm(any());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@UnittestCaseInfo(/* ... */)
|
|
217
|
+
@Test
|
|
218
|
+
public void testBeforeDoOperation_openProPlanSel_dataExists() {
|
|
219
|
+
//step 采集主体=工序计划,点击选择弹窗,待采集产品序列号分录存在数据
|
|
220
|
+
this.mockModelGetCollectSubject(CollectSubjectEnum.PROPLAN);
|
|
221
|
+
DynamicObjectCollection prodSnEntry = this.mockModelGetProdSnEntry(1);
|
|
222
|
+
prodSnEntry.add(new DynamicObjectMocker()
|
|
223
|
+
.add(ENTRYENTITY_PRODSN.PROPLANENTRY_ID, 0L).getObject());
|
|
224
|
+
keyPartCollectHelper.when(KeypartCollectHelper::buildProcessPlanSubjectFilter)
|
|
225
|
+
.thenReturn(QFilter.of("1=1"));
|
|
226
|
+
BeforeDoOperationEventArgs recordsetting =
|
|
227
|
+
mockBeforeDoOperationEventArgs(OP.SELPROPLAN);
|
|
228
|
+
plugin.beforeDoOperation(recordsetting);
|
|
229
|
+
//assert 执行选择弹窗,覆盖排除当前已存在工序计划分录的过滤条件
|
|
230
|
+
verify(testView).showForm(any());
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**要点**:
|
|
235
|
+
- 方法名格式:`test<方法名>_<操作代码>`(如 `testBeforeDoOperation_openRecord`)
|
|
236
|
+
- 使用 BasePluginTest 提供的 `mockBeforeDoOperationEventArgs(opKey)` 构造入参
|
|
237
|
+
- 每个操作代码独立为一个测试方法
|
|
238
|
+
- 同一操作代码的不同数据场景可合并在同一方法中,方法名追加场景后缀(如 `_dataExists`)
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 4. If/Else 分支覆盖(条件笛卡尔积合并在同一方法)
|
|
243
|
+
|
|
244
|
+
```java
|
|
245
|
+
@UnittestCaseInfo(/* ... */)
|
|
246
|
+
@Test
|
|
247
|
+
public void testRefreshProdSnEntry_kpcCacheExists() {
|
|
248
|
+
//step 缓存中添加已存在1条数据
|
|
249
|
+
List<KeyPartCollectDetail> cacheData = Stream.of(
|
|
250
|
+
new KeyPartCollectDetail("testuuid", 1L, 1L, 1L, "sn001",
|
|
251
|
+
"bn001", 1L, BigDecimal.ONE))
|
|
252
|
+
.collect(Collectors.toList());
|
|
253
|
+
mockPageCacheGet("key_part_collect_detail_cache",
|
|
254
|
+
SerializationUtils.toJsonString(cacheData));
|
|
255
|
+
this.mockKpcEntryDisplayName("testname");
|
|
256
|
+
this.mockModelGetKeyPartEntry(0, 0);
|
|
257
|
+
|
|
258
|
+
//step 操作编码不存在
|
|
259
|
+
BeforeDoOperationEventArgs beforeDoOperationEventArgs =
|
|
260
|
+
mockBeforeDoOperationEventArgs("testOp");
|
|
261
|
+
plugin.beforeDoOperation(beforeDoOperationEventArgs);
|
|
262
|
+
|
|
263
|
+
//step 设置操作编码=刷新
|
|
264
|
+
beforeDoOperationEventArgs = mockBeforeDoOperationEventArgs(OP.REFRESH);
|
|
265
|
+
plugin.beforeDoOperation(beforeDoOperationEventArgs);
|
|
266
|
+
//assert 采集信息已存在,弹出确认提示
|
|
267
|
+
verify(testView).showConfirm(anyString(), anyString(),
|
|
268
|
+
eq(MessageBoxOptions.OKCancel), eq(ConfirmTypes.Save),
|
|
269
|
+
any(ConfirmCallBackListener.class), any());
|
|
270
|
+
|
|
271
|
+
//step 覆盖提示语判断分支(displayName=null)
|
|
272
|
+
this.mockKpcEntryDisplayName(null);
|
|
273
|
+
plugin.beforeDoOperation(beforeDoOperationEventArgs);
|
|
274
|
+
//assert 采集信息已存在,弹出确认提示(共2次)
|
|
275
|
+
verify(testView, times(2)).showConfirm(anyString(), anyString(),
|
|
276
|
+
eq(MessageBoxOptions.OKCancel), eq(ConfirmTypes.Save),
|
|
277
|
+
any(ConfirmCallBackListener.class), any());
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**要点**:
|
|
282
|
+
- 多个 if/else 分支条件在同一方法中依次设置不同数据并调用
|
|
283
|
+
- 使用 `verify(mock, times(N))` 精确验证调用次数
|
|
284
|
+
- 使用 `//step` 和 `//assert` 注释区分测试步骤与断言
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 5. PropertyChanged(值改变事件测试)
|
|
289
|
+
|
|
290
|
+
```java
|
|
291
|
+
@UnittestCaseInfo(/* ... */)
|
|
292
|
+
@Test
|
|
293
|
+
public void testPropertyChanged_collectSubject() {
|
|
294
|
+
CollectSubjectEnum targetSubject = CollectSubjectEnum.PROPLAN;
|
|
295
|
+
keyPartCollectHelper.when(() -> KeypartCollectHelper
|
|
296
|
+
.saveCollectSubjectToUserOption(anyString()))
|
|
297
|
+
.thenAnswer(i -> null);
|
|
298
|
+
PropertyChangedArgs args = mockPropertyChangedArgs(
|
|
299
|
+
KeyPartCollectConst.COLLECTSUBJECT, null, targetSubject, -1);
|
|
300
|
+
|
|
301
|
+
//step 获取到的采集主体为空
|
|
302
|
+
this.mockModelGetCollectSubject(null);
|
|
303
|
+
plugin.propertyChanged(args);
|
|
304
|
+
//assert 执行清理操作,未更新标题
|
|
305
|
+
verify(testModel).deleteEntryData(eq(ENTRYENTITY_PRODSN.ENTRYENTITY));
|
|
306
|
+
verify(testModel).deleteEntryData(eq(ENTRYENTITY_KEYPART.ENTRYENTITY));
|
|
307
|
+
verify(pageCache).remove(eq("key_part_collect_detail_cache"));
|
|
308
|
+
verify(testView, Mockito.never())
|
|
309
|
+
.updateControlMetadata(eq(CONTROL.ADVCONAP_TOCOLLECT), anyMap());
|
|
310
|
+
|
|
311
|
+
//step 获取到的采集主体不为空
|
|
312
|
+
this.mockModelGetCollectSubject(targetSubject);
|
|
313
|
+
plugin.propertyChanged(args);
|
|
314
|
+
//assert 设置待采集信息标题
|
|
315
|
+
verify(testView).updateControlMetadata(eq(CONTROL.ADVCONAP_TOCOLLECT), anyMap());
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@UnittestCaseInfo(/* ... */)
|
|
319
|
+
@Test
|
|
320
|
+
public void testPropertyChanged_keySn_oldValueEmpty() {
|
|
321
|
+
//step 序列号由空值到非空,子项单位与物料基本单位一致
|
|
322
|
+
DynamicObjectCollection keyPartEntry = this.mockModelGetKeyPartEntry(1, 2);
|
|
323
|
+
when(testModel.getEntryRowEntity(eq(ENTRYENTITY_KEYPART.ENTRYENTITY), anyInt()))
|
|
324
|
+
.thenReturn(keyPartEntry.get(0));
|
|
325
|
+
when(testModel.getEntryCurrentRowIndex(eq(ENTRYENTITY_KEYPART.ENTRYENTITY)))
|
|
326
|
+
.thenReturn(0);
|
|
327
|
+
when(testModel.getValue(eq(SUBENTRYENTITY_COLL.KEYSN), anyInt(), anyInt()))
|
|
328
|
+
.thenReturn("testKeySn");
|
|
329
|
+
when(testModel.getValue(eq(ENTRYENTITY_KEYPART.CHILDMATUNIT_ID), anyInt()))
|
|
330
|
+
.thenReturn(1L);
|
|
331
|
+
this.mockModelGetMatMaster(1L);
|
|
332
|
+
|
|
333
|
+
PropertyChangedArgs args = mockPropertyChangedArgs(
|
|
334
|
+
SUBENTRYENTITY_COLL.KEYSN, null, "testsn", 0);
|
|
335
|
+
plugin.propertyChanged(args);
|
|
336
|
+
//assert 此次采集数量不可编辑,默认值=1
|
|
337
|
+
verify(testView).setEnable(eq(false), eq(0), eq(SUBENTRYENTITY_COLL.COLLECTQTY));
|
|
338
|
+
verify(testModel).setValue(eq(SUBENTRYENTITY_COLL.COLLECTQTY),
|
|
339
|
+
eq(BigDecimal.ONE), eq(0), eq(0));
|
|
340
|
+
|
|
341
|
+
//step 子项单位与物料基本单位不一致,执行单位换算
|
|
342
|
+
keyPartCollectHelper.when(() -> KeypartCollectHelper
|
|
343
|
+
.calcNewQty(eq(BigDecimal.ONE), anyLong(), anyLong(), anyLong()))
|
|
344
|
+
.thenReturn(BigDecimal.TEN);
|
|
345
|
+
when(testModel.getValue(eq(ENTRYENTITY_KEYPART.CHILDMATUNIT_ID), anyInt()))
|
|
346
|
+
.thenReturn(2L);
|
|
347
|
+
plugin.propertyChanged(args);
|
|
348
|
+
//assert 采集数量按换算后的值设置
|
|
349
|
+
verify(testModel).setValue(eq(SUBENTRYENTITY_COLL.COLLECTQTY),
|
|
350
|
+
eq(BigDecimal.TEN), eq(0), eq(0));
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**要点**:
|
|
355
|
+
- 使用 BasePluginTest 提供的 `mockPropertyChangedArgs(fieldName, oldValue, newValue, rowIndex)` 构造入参
|
|
356
|
+
- 子单据体属性值变化额外传 parentEntryName: `mockPropertyChangedArgs(field, old, new, row, parentEntry)`
|
|
357
|
+
- 同一属性不同值变化场景拆分为独立方法(如 `_oldValueEmpty`、`_oldValueNotEmpty`、`_newValueEmpty`)
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 6. ClosedCallBack(弹窗回调测试)
|
|
362
|
+
|
|
363
|
+
```java
|
|
364
|
+
@UnittestCaseInfo(/* ... */)
|
|
365
|
+
@Test
|
|
366
|
+
public void testProdSnSelCallback() {
|
|
367
|
+
//step 构造工序计划F7已选数据
|
|
368
|
+
ProsnReturnData d1 = new ProsnReturnData();
|
|
369
|
+
d1.setProplanEntryId(1L);
|
|
370
|
+
// ... 其余测试数据省略
|
|
371
|
+
List<ProsnReturnData> returnData = Stream.of(d1).collect(Collectors.toList());
|
|
372
|
+
//step 采集主体=工序产品序列号
|
|
373
|
+
this.mockModelGetCollectSubject(CollectSubjectEnum.PROPLAN_SN);
|
|
374
|
+
this.mockModelGetProdSnEntry(0);
|
|
375
|
+
mockBatchCreateNewEntryRow(ENTRYENTITY_PRODSN.ENTRYENTITY, 0);
|
|
376
|
+
|
|
377
|
+
ClosedCallBackEvent evt = mockClosedCallBackEvent(OP.SELPRODSN, returnData);
|
|
378
|
+
plugin.closedCallBack(evt);
|
|
379
|
+
//assert 合法数据仅1条,并对待采集产品序列号行执行赋值
|
|
380
|
+
verify(testModel).setValue(eq(ENTRYENTITY_PRODSN.SN), anyString(), anyInt());
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**要点**:
|
|
385
|
+
- 使用 BasePluginTest 提供的 `mockClosedCallBackEvent(actionId, returnData)` 构造回调事件
|
|
386
|
+
- 不同 callbackActionId 的回调拆分为独立方法
|
|
387
|
+
- 空数据 / 非法数据场景单独测试(如 `_invalidReturn`、`_emptySelection`)
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 7. ConfirmCallBack(消息确认回调测试)
|
|
392
|
+
|
|
393
|
+
```java
|
|
394
|
+
@UnittestCaseInfo(/* ... */)
|
|
395
|
+
@Test
|
|
396
|
+
public void testConfirmCallBack_refresh_includeCurrentRow() {
|
|
397
|
+
//step 确认刷新,且待采集产品明细当前行在要刷新的行中
|
|
398
|
+
MessageBoxClosedEvent e = super.mockMessageBoxClosedEvent(
|
|
399
|
+
OP.REFRESH, MessageBoxResult.Yes);
|
|
400
|
+
this.mockModelGetProdSnEntry(1);
|
|
401
|
+
when(testModel.getEntryCurrentRowIndex(eq(ENTRYENTITY_PRODSN.ENTRYENTITY)))
|
|
402
|
+
.thenReturn(0);
|
|
403
|
+
when(testModel.getValue(eq(ENTRYENTITY_PRODSN.UUID), anyInt()))
|
|
404
|
+
.thenReturn("testuuid");
|
|
405
|
+
this.mockModelGetCollectSubject(null);
|
|
406
|
+
super.mockGetControl(ENTRYENTITY_PRODSN.ENTRYENTITY, EntryGrid.class);
|
|
407
|
+
plugin.confirmCallBack(e);
|
|
408
|
+
//assert 当前行的采集明细缓存清除;清空当前关键件分录
|
|
409
|
+
verify(pageCache).remove(eq("key_part_collect_detail_cache"));
|
|
410
|
+
verify(testModel).deleteEntryData(eq(ENTRYENTITY_KEYPART.ENTRYENTITY));
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**要点**:
|
|
415
|
+
- 使用 BasePluginTest 的 `mockMessageBoxClosedEvent(callbackId, result)` 构造事件
|
|
416
|
+
- `MessageBoxResult.Yes` / `MessageBoxResult.Cancel` 分别测试确认/取消路径
|
|
417
|
+
- 同一回调的不同场景用方法名后缀区分(如 `_includeCurrentRow`、`_notCurrentRow`)
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## 8. RegisterListener(监听注册测试)
|
|
422
|
+
|
|
423
|
+
```java
|
|
424
|
+
@UnittestCaseInfo(/* ... */)
|
|
425
|
+
@Test
|
|
426
|
+
public void testRegisterListener() {
|
|
427
|
+
//step 对待采集产品序列号分录、关键件分录执行监听
|
|
428
|
+
EventObject e = mock(EventObject.class);
|
|
429
|
+
EntryGrid prodEg = mockGetControl(ENTRYENTITY_PRODSN.ENTRYENTITY, EntryGrid.class);
|
|
430
|
+
EntryGrid kpEg = mockGetControl(ENTRYENTITY_KEYPART.ENTRYENTITY, EntryGrid.class);
|
|
431
|
+
plugin.registerListener(e);
|
|
432
|
+
//assert 添加当前插件作为监听器
|
|
433
|
+
verify(prodEg).addRowClickListener(any(KeyPartCollectPlugin.class));
|
|
434
|
+
verify(kpEg).addRowClickListener(any(KeyPartCollectPlugin.class));
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**要点**:
|
|
439
|
+
- 使用 BasePluginTest 的 `mockGetControl(key, Class)` 获取控件 mock
|
|
440
|
+
- 验证插件实例被正确注册为监听器
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## 9. 私有方法测试(ReflectHelper)
|
|
445
|
+
|
|
446
|
+
```java
|
|
447
|
+
@UnittestCaseInfo(/* ... */)
|
|
448
|
+
@Test
|
|
449
|
+
public void testGetFailedRowIndexes() {
|
|
450
|
+
//step 参数successRow为空
|
|
451
|
+
int[] totalRows = {0, 1, 2, 3, 4, 5};
|
|
452
|
+
Object result = ReflectHelper.invokeProtectedMethod(
|
|
453
|
+
KeyPartCollectPlugin.class, plugin,
|
|
454
|
+
"getFailedRowIndexes", totalRows, null);
|
|
455
|
+
//assert 返回totalRows
|
|
456
|
+
assertEquals(totalRows, result);
|
|
457
|
+
|
|
458
|
+
//step 参数successRow不为空,元素与totalRows相同
|
|
459
|
+
int[] successRows = {0, 1, 2, 3, 4, 5};
|
|
460
|
+
result = ReflectHelper.invokeProtectedMethod(
|
|
461
|
+
KeyPartCollectPlugin.class, plugin,
|
|
462
|
+
"getFailedRowIndexes", totalRows, successRows);
|
|
463
|
+
//assert 返回结果为空
|
|
464
|
+
assertNotNull(result);
|
|
465
|
+
assertTrue(ObjectUtils.isEmpty(result));
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**要点**:
|
|
470
|
+
- 使用 `ReflectHelper.invokeProtectedMethod(Class, instance, methodName, args...)` 调用私有/受保护方法
|
|
471
|
+
- 适用于无法通过公共方法间接覆盖的私有逻辑
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## 10. AfterDoOperation(操作后事件)
|
|
476
|
+
|
|
477
|
+
```java
|
|
478
|
+
@UnittestCaseInfo(/* ... */)
|
|
479
|
+
@Test
|
|
480
|
+
public void testAfterDoOperation_confirmCollect_finalSuccess() {
|
|
481
|
+
//step 返回保存成功、最终操作成功
|
|
482
|
+
AfterDoOperationEventArgs args = mockAfterDoOperationEventArgs(OP.CONFIRM);
|
|
483
|
+
when(pageCache.get(eq("key_part_collect_detail_cache"))).thenReturn(null);
|
|
484
|
+
DynamicObjectCollection keyPartEntries = this.mockModelGetKeyPartEntry(1, 2);
|
|
485
|
+
this.mockModelGetCollectSubject(CollectSubjectEnum.PROPLAN);
|
|
486
|
+
this.mockModelGetProdSnEntry(1);
|
|
487
|
+
when(testModel.getEntryEntity(eq(SUBENTRYENTITY_COLL.ENTITY)))
|
|
488
|
+
.thenReturn(keyPartEntries.get(0)
|
|
489
|
+
.getDynamicObjectCollection(SUBENTRYENTITY_COLL.ENTITY));
|
|
490
|
+
List<DynamicObject> mockBills = this.mockRecordBills();
|
|
491
|
+
keyPartCollectHelper.when(() -> KeypartCollectHelper
|
|
492
|
+
.buildRecordBillWhenConfirmCollect(
|
|
493
|
+
any(CollectSubjectEnum.class),
|
|
494
|
+
any(DynamicObjectCollection.class), anyList()))
|
|
495
|
+
.thenReturn(mockBills);
|
|
496
|
+
OperationResult opResult = new OperationResult();
|
|
497
|
+
KeyPartConfirmResult mockResult = new KeyPartConfirmResult(opResult, opResult);
|
|
498
|
+
keyPartCollectHelper.when(() -> KeypartCollectHelper
|
|
499
|
+
.saveRecordsToAudit(anyList())).thenReturn(mockResult);
|
|
500
|
+
plugin.afterDoOperation(args);
|
|
501
|
+
//assert 最终操作成功,弹出成功提示
|
|
502
|
+
verify(testView).showSuccessNotification(anyString());
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## BasePluginTest 提供的常用工具一览
|
|
509
|
+
|
|
510
|
+
| 工具方法/变量 | 说明 |
|
|
511
|
+
|---|---|
|
|
512
|
+
| `plugin` | 被测插件实例(由 `createPlugin()` 返回) |
|
|
513
|
+
| `testView` | IFormView mock |
|
|
514
|
+
| `testModel` | IDataModel mock |
|
|
515
|
+
| `pageCache` | PageCache mock |
|
|
516
|
+
| `mockBeforeDoOperationEventArgs(opKey)` | 构造 BeforeDoOperationEventArgs |
|
|
517
|
+
| `mockAfterDoOperationEventArgs(opKey)` | 构造 AfterDoOperationEventArgs |
|
|
518
|
+
| `mockPropertyChangedArgs(field, old, new, row)` | 构造 PropertyChangedArgs |
|
|
519
|
+
| `mockPropertyChangedArgs(field, old, new, row, parentEntry)` | 构造子单据体 PropertyChangedArgs |
|
|
520
|
+
| `mockClosedCallBackEvent(actionId, returnData)` | 构造 ClosedCallBackEvent |
|
|
521
|
+
| `mockMessageBoxClosedEvent(callbackId, result)` | 构造 MessageBoxClosedEvent |
|
|
522
|
+
| `mockClientCallBackEvent(key, data)` | 构造 ClientCallBackEvent |
|
|
523
|
+
| `mockGetControl(key, Class)` | 获取控件 mock |
|
|
524
|
+
| `mockRowClickEvent(entryKey, row, isEmpty)` | 构造 RowClickEvent |
|
|
525
|
+
| `mockAfterAddRowEventArgs(entryKey, rows, data)` | 构造 AfterAddRowEventArgs |
|
|
526
|
+
| `mockBeforeDeleteRowEventArgs(entryKey, rows)` | 构造 BeforeDeleteRowEventArgs |
|
|
527
|
+
| `mockAfterDeleteRowEventArgs(entryKey)` | 构造 AfterDeleteRowEventArgs |
|
|
528
|
+
| `mockBeforeFieldPostBackEvent(field, row, ctrlClass, value)` | 构造 BeforeFieldPostBackEvent |
|
|
529
|
+
| `mockBatchCreateNewEntryRow(entryKey, startIndex)` | mock 批量增行 |
|
|
530
|
+
| `mockPageCacheGet(key, value)` | mock pageCache.get |
|
|
531
|
+
| `mockEventObject(data)` | 构造 EventObject |
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## 要点总结
|
|
536
|
+
|
|
537
|
+
### 类结构
|
|
538
|
+
- 继承 `BasePluginTest<被测插件类>`,实现 `createPlugin()` 工厂方法
|
|
539
|
+
- MockedStatic 变量在 `@Before` 初始化、`@After` 释放
|
|
540
|
+
- 公共辅助 mock 方法抽取到类级别复用
|
|
541
|
+
|
|
542
|
+
### Switch 分支
|
|
543
|
+
- **每个操作码对应独立测试方法**,方法名包含操作标识
|
|
544
|
+
- 同一操作码的不同数据场景追加后缀(如 `_dataExists`、`_emptyRow`)
|
|
545
|
+
|
|
546
|
+
### If/Else 分支
|
|
547
|
+
- 条件组合(笛卡尔积)可合并在同一方法中依次测试
|
|
548
|
+
- 每个分支用 `//step` 注释说明条件设置,`//assert` 验证结果
|
|
549
|
+
- 使用 `verify(mock, times(N))` 精确断言调用次数
|
|
550
|
+
|
|
551
|
+
### DynamicObject 断言
|
|
552
|
+
- `getString()` 不为 null,空值用 `assertEquals("", ...)`
|
|
553
|
+
- `getLong()` 不为 null,空值用 `assertEquals(0L, ...)`
|
|
554
|
+
- `getBigDecimal()` 不为 null,空值用 `assertEquals(BigDecimal.ZERO, ...)`
|
|
555
|
+
- `getDate()` 可能为 null,需 `assertNull` 或 `assertNotNull`
|
|
556
|
+
|
|
557
|
+
### 常量 & 枚举
|
|
558
|
+
- 所有字段标识、操作码、回调 ID 均引用常量类(如 `OP.OPENRECORD`、`ENTRYENTITY_PRODSN.UUID`)
|
|
559
|
+
- 禁止使用字面值字符串替代常量引用
|
|
560
|
+
- 找不到常量/枚举时必须告知用户,由用户补充
|