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,235 @@
|
|
|
1
|
+
# FormPlugin 表单插件测试模式
|
|
2
|
+
|
|
3
|
+
## 适用范围
|
|
4
|
+
|
|
5
|
+
`<模块组>-formplugin` 工程中继承以下基类的插件类:
|
|
6
|
+
- `kd.bos.form.plugin.AbstractFormPlugin`(表单插件)
|
|
7
|
+
- `kd.bos.list.plugin.AbstractListPlugin`(列表插件)
|
|
8
|
+
- `kd.bos.bill.plugin.AbstractBillPlugin`(单据插件)
|
|
9
|
+
|
|
10
|
+
这些插件在表单/列表/单据生命周期(打开、值更新、按钮点击等)的各阶段介入处理。
|
|
11
|
+
|
|
12
|
+
## 测试文件位置
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
<模块组>-formplugin/src/test/java/kd/<包路径>/formplugin/{subpackage}/XxxPluginTest.java
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
与被测类保持相同的包路径。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 基类与参数获取
|
|
23
|
+
|
|
24
|
+
### 优先级链
|
|
25
|
+
|
|
26
|
+
formplugin 测试的基类和参数获取遵循以下优先级:
|
|
27
|
+
|
|
28
|
+
1. **优先从 BasePluginTest 获取**
|
|
29
|
+
- 在 `<模块组>-formplugin/src/test/java/` 下搜索 `BasePluginTest`
|
|
30
|
+
- 该基类封装了 AbstractFormPlugin 测试所需的通用参数和 mock 取値逻辑(如 IFormView、IDataModel、表单上下文等)
|
|
31
|
+
|
|
32
|
+
2. **参考同包下已有测试类**
|
|
33
|
+
- 路径:`<模块组>-formplugin/src/test/java/kd/<包路径>/formplugin/` 及其子包
|
|
34
|
+
- 查找以 `Test` 结尾的类,参考其中的类似写法
|
|
35
|
+
|
|
36
|
+
3. **降级到苍穹公共单测框架**
|
|
37
|
+
- 若以上均无可参考案例,使用苍穹公共单测框架
|
|
38
|
+
- **必须主动告知用户**缺失的取值逻辑,由用户补充到 BasePluginTest
|
|
39
|
+
|
|
40
|
+
### 使用方式
|
|
41
|
+
|
|
42
|
+
```java
|
|
43
|
+
package kd.<包路径>.formplugin.subpackage;
|
|
44
|
+
|
|
45
|
+
// 继承 BasePluginTest 获取通用 mock 和参数
|
|
46
|
+
public class XxxPluginTest extends BasePluginTest<XxxPlugin> {
|
|
47
|
+
// BasePluginTest 已预置的 mock 和参数可直接使用
|
|
48
|
+
// 额外需要的 mock 在此声明
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 表单插件生命周期方法
|
|
55
|
+
|
|
56
|
+
| 方法 | 触发时机 | 测试重点 |
|
|
57
|
+
|------|---------|---------|
|
|
58
|
+
| `afterCreateNewData` | 新建单据后 | 字段默认值设置 |
|
|
59
|
+
| `afterLoadData` | 数据加载后 | 界面控件状态设置 |
|
|
60
|
+
| `afterBindData` | 数据绑定后 | 控件可见性/可编辑性 |
|
|
61
|
+
| `beforeDoOperation` | 操作执行前 | 前置校验逻辑 |
|
|
62
|
+
| `propertyChanged` | 字段值变更 | 联动逻辑(switch/if 分支) |
|
|
63
|
+
| `itemClick` | 按钮/菜单点击 | 按钮响应逻辑 |
|
|
64
|
+
| `beforeClosed` | 表单关闭前 | 清理逻辑 |
|
|
65
|
+
| `closedCallBack` | 子表单关闭回调 | 回调处理逻辑 |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 模板结构
|
|
70
|
+
|
|
71
|
+
```java
|
|
72
|
+
package kd.<包路径>.formplugin.subpackage;
|
|
73
|
+
|
|
74
|
+
import kd.bos.dataentity.entity.DynamicObject;
|
|
75
|
+
import kd.bos.form.plugin.AbstractFormPlugin;
|
|
76
|
+
import kd.bos.test.ext.annotaions.UnittestCaseInfo;
|
|
77
|
+
import kd.bos.form.unittest.DisplayName;
|
|
78
|
+
import kd.bos.unittest.mock.DynamicObjectMocker;
|
|
79
|
+
import org.junit.After;
|
|
80
|
+
import org.junit.Before;
|
|
81
|
+
import org.junit.Test;
|
|
82
|
+
import org.mockito.MockedStatic;
|
|
83
|
+
import org.mockito.Mockito;
|
|
84
|
+
|
|
85
|
+
import static org.junit.Assert.*;
|
|
86
|
+
import static org.mockito.ArgumentMatchers.*;
|
|
87
|
+
import static org.mockito.Mockito.*;
|
|
88
|
+
|
|
89
|
+
public class XxxPluginTest extends BasePluginTest<XxxPlugin> {
|
|
90
|
+
|
|
91
|
+
// 1. 声明额外需要 Mock 的静态类(BasePluginTest 未覆盖的)
|
|
92
|
+
private MockedStatic<SomeHelper> someHelper;
|
|
93
|
+
|
|
94
|
+
@Before
|
|
95
|
+
public void before() {
|
|
96
|
+
// 若 BasePluginTest 有 @Before,调用 super.before()
|
|
97
|
+
someHelper = Mockito.mockStatic(SomeHelper.class);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@After
|
|
101
|
+
public void after() {
|
|
102
|
+
// 若 BasePluginTest 有 @After,调用 super.after()
|
|
103
|
+
someHelper.close();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ========== propertyChanged 方法测试(switch 分支拆分)==========
|
|
107
|
+
|
|
108
|
+
@UnittestCaseInfo(
|
|
109
|
+
author = "<name> <<name>@kingdee.com>",
|
|
110
|
+
title = "Test propertyChanged when field is materialId",
|
|
111
|
+
targetClass = "kd.<包路径>.formplugin.subpackage.XxxPlugin",
|
|
112
|
+
targetMethod = "propertychanged",
|
|
113
|
+
lastUpdateTime = "yyyy-MM-dd HH:mm:ss",
|
|
114
|
+
lastUpdateAuthor = "<name> <<name>@kingdee.com>",
|
|
115
|
+
methodSignature = "public void propertyChanged(PropertyChangedArgs e)",
|
|
116
|
+
testPoints = {"Functionality"},
|
|
117
|
+
description = "Test propertyChanged switch case: materialId"
|
|
118
|
+
)
|
|
119
|
+
@Test
|
|
120
|
+
@DisplayName("propertyChanged - switch case: materialId 字段变更")
|
|
121
|
+
public void testPropertyChanged_CaseMaterialId() {
|
|
122
|
+
//step 构建 PropertyChangedArgs,fieldKey = "materialId"
|
|
123
|
+
// ... 使用 BasePluginTest 提供的方式构建参数
|
|
124
|
+
|
|
125
|
+
//step 执行被测方法
|
|
126
|
+
XxxPlugin plugin = new XxxPlugin();
|
|
127
|
+
// ... 设置插件上下文
|
|
128
|
+
// plugin.propertyChanged(args);
|
|
129
|
+
|
|
130
|
+
//assert 验证联动结果
|
|
131
|
+
// assertEquals(expectedValue, ...);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@UnittestCaseInfo(
|
|
135
|
+
author = "<name> <<name>@kingdee.com>",
|
|
136
|
+
title = "Test propertyChanged when field is qty",
|
|
137
|
+
targetClass = "kd.<包路径>.formplugin.subpackage.XxxPlugin",
|
|
138
|
+
targetMethod = "propertychanged",
|
|
139
|
+
lastUpdateTime = "yyyy-MM-dd HH:mm:ss",
|
|
140
|
+
lastUpdateAuthor = "<name> <<name>@kingdee.com>",
|
|
141
|
+
methodSignature = "public void propertyChanged(PropertyChangedArgs e)",
|
|
142
|
+
testPoints = {"Functionality"},
|
|
143
|
+
description = "Test propertyChanged switch case: qty"
|
|
144
|
+
)
|
|
145
|
+
@Test
|
|
146
|
+
@DisplayName("propertyChanged - switch case: qty 字段变更")
|
|
147
|
+
public void testPropertyChanged_CaseQty() {
|
|
148
|
+
//step 构建 PropertyChangedArgs,fieldKey = "qty"
|
|
149
|
+
// ...
|
|
150
|
+
|
|
151
|
+
//step 执行被测方法
|
|
152
|
+
// ...
|
|
153
|
+
|
|
154
|
+
//assert 验证联动结果
|
|
155
|
+
// ...
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ========== 判断分支覆盖(笛卡尔积)==========
|
|
159
|
+
|
|
160
|
+
@Test
|
|
161
|
+
@DisplayName("beforeDoOperation - 条件组合覆盖")
|
|
162
|
+
public void testBeforeDoOperation_ConditionCombinations() {
|
|
163
|
+
//step 条件组合1: status=A, hasEntry=true -> 允许操作
|
|
164
|
+
// ...
|
|
165
|
+
//assert
|
|
166
|
+
// ...
|
|
167
|
+
|
|
168
|
+
//step 条件组合2: status=A, hasEntry=false -> 阻止操作
|
|
169
|
+
// ...
|
|
170
|
+
//assert
|
|
171
|
+
// ...
|
|
172
|
+
|
|
173
|
+
//step 条件组合3: status=B, hasEntry=true -> 阻止操作
|
|
174
|
+
// ...
|
|
175
|
+
//assert
|
|
176
|
+
// ...
|
|
177
|
+
|
|
178
|
+
//step 条件组合4: status=B, hasEntry=false -> 阻止操作
|
|
179
|
+
// ...
|
|
180
|
+
//assert
|
|
181
|
+
// ...
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 特殊 Mock 要求
|
|
189
|
+
|
|
190
|
+
> **DB 类 ClassPool defrost** 和 **ProcessSettleMockHelper** 是所有工程通用的特殊 Mock 规则,详见 SKILL.md 2.6 / 2.7 节。此处不再重复,formplugin 工程同样遵循。
|
|
191
|
+
|
|
192
|
+
### MockedStatic 生命周期规则
|
|
193
|
+
|
|
194
|
+
| 场景 | 方式 |
|
|
195
|
+
|------|------|
|
|
196
|
+
| 同一静态类在 > 1 个测试方法中使用 | 定义为**成员变量**,@Before 赋值,@After close |
|
|
197
|
+
| 同一静态类仅在 1 个测试方法中使用 | 使用 **try-with-resources** 局部 mock |
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## DynamicObject 断言规范
|
|
202
|
+
|
|
203
|
+
对 DynamicObject 取值进行断言时的默认值规则:
|
|
204
|
+
|
|
205
|
+
| 取值方法 | 默认值(字段未赋值时) | 断言写法 |
|
|
206
|
+
|---------|---------------------|---------|
|
|
207
|
+
| `getString(key)` | `""` (空字符串,不为 null) | `assertEquals("", obj.getString("field"))` |
|
|
208
|
+
| `getLong(key)` | `0L` | `assertEquals(0L, obj.getLong("field"))` |
|
|
209
|
+
| `getInt(key)` | `0` | `assertEquals(0, obj.getInt("field"))` |
|
|
210
|
+
| `getBigDecimal(key)` | `BigDecimal.ZERO` | `assertEquals(BigDecimal.ZERO, obj.getBigDecimal("field"))` |
|
|
211
|
+
| `getBoolean(key)` | `false` | `assertFalse(obj.getBoolean("field"))` |
|
|
212
|
+
| `getDate(key)` | **可能为 null** | `assertNull(obj.getDate("field"))` 或具体日期值 |
|
|
213
|
+
|
|
214
|
+
**原则:除 getDate 外,DynamicObject 的 getter 方法默认不返回 null,使用基本值断言。**
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 常量/枚举类引用规则
|
|
219
|
+
|
|
220
|
+
- 测试代码中引用常量、枚举时,**必须使用原始常量类/枚举类的引用**(如 `StatusEnum.APPROVED`),而非字面值(如 `"A"`)
|
|
221
|
+
- 若常量类/枚举类**无法找到**(如未在 classpath 中、编译报错等),**必须主动告知用户**,由用户补充:
|
|
222
|
+
- 文件路径
|
|
223
|
+
- 或完整源码
|
|
224
|
+
- **禁止**忽略使用逻辑或用字面值替代
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 关键要点总结
|
|
229
|
+
|
|
230
|
+
1. **基类优先级**:BasePluginTest -> 同包 *Test 类参考 -> 苍穹公共框架(缺失告知用户)
|
|
231
|
+
2. **switch 必须拆方法**:propertyChanged / itemClick 等含 switch 的方法,每个 case 独立测试方法
|
|
232
|
+
3. **判断分支笛卡尔积**:if/else 条件组合需全面覆盖,可合并到一个方法中
|
|
233
|
+
4. **DB 类 / ProcessSettleMockHelper**:通用规则,详见 SKILL.md 2.6 / 2.7 节
|
|
234
|
+
5. **DynamicObject 断言**:getString/getLong/getBigDecimal 等用基本值断言,getDate 除外
|
|
235
|
+
6. **常量/枚举**:禁止字面值替代,找不到就告知用户
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# OpPlugin 操作插件测试模式
|
|
2
|
+
|
|
3
|
+
## 适用范围
|
|
4
|
+
|
|
5
|
+
`<模块组>-opplugin` 工程中继承 `AbstractOperationServicePlugIn` 的操作插件类。
|
|
6
|
+
操作插件在单据操作(保存/提交/审核等)的各生命周期阶段介入处理。
|
|
7
|
+
|
|
8
|
+
## 测试文件位置
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
<模块组>-opplugin/src/test/java/kd/<包路径>/opplugin/{module}/XxxOpTest.java
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 操作插件生命周期方法
|
|
15
|
+
|
|
16
|
+
| 方法 | 触发时机 | 测试重点 |
|
|
17
|
+
|------|---------|---------|
|
|
18
|
+
| `onPreparePropertys` | 准备加载字段 | 验证字段键列表 |
|
|
19
|
+
| `onAddValidators` | 添加校验器 | 验证注册的校验器 |
|
|
20
|
+
| `beforeExecuteOperationTransaction` | 事务前 | 验证前置逻辑 |
|
|
21
|
+
| `beginOperationTransaction` | 事务开始 | 验证核心业务逻辑 |
|
|
22
|
+
| `endOperationTransaction` | 事务结束 | 验证后置处理 |
|
|
23
|
+
| `afterExecuteOperationTransaction` | 事务后 | 验证收尾逻辑 |
|
|
24
|
+
|
|
25
|
+
## 基类选择
|
|
26
|
+
|
|
27
|
+
| 场景 | 基类 |
|
|
28
|
+
|------|------|
|
|
29
|
+
| 简单操作插件 | `AbstractJunitNoDependenciesResManagerTest` |
|
|
30
|
+
| 需要通用操作插件框架 | 继承 `OpBaseTest`(提供 registerOpPlugin 方法) |
|
|
31
|
+
|
|
32
|
+
## 模板结构(直接方式)
|
|
33
|
+
|
|
34
|
+
```java
|
|
35
|
+
package kd.<包路径>.opplugin.modulename;
|
|
36
|
+
|
|
37
|
+
import kd.bos.dataentity.OperateOption;
|
|
38
|
+
import kd.bos.dataentity.entity.DynamicObject;
|
|
39
|
+
import kd.bos.entity.operate.result.OperationResult;
|
|
40
|
+
import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
|
|
41
|
+
import kd.bos.form.unittest.DisplayName;
|
|
42
|
+
import kd.bos.test.ext.annotaions.UnittestCaseInfo;
|
|
43
|
+
import kd.bos.unittest.AbstractJunitNoDependenciesResManagerTest;
|
|
44
|
+
import kd.bos.unittest.mock.DynamicObjectMocker;
|
|
45
|
+
import org.junit.After;
|
|
46
|
+
import org.junit.Before;
|
|
47
|
+
import org.junit.Test;
|
|
48
|
+
import org.mockito.MockedStatic;
|
|
49
|
+
|
|
50
|
+
import static org.mockito.ArgumentMatchers.*;
|
|
51
|
+
import static org.mockito.Mockito.*;
|
|
52
|
+
|
|
53
|
+
public class XxxOpTest extends AbstractJunitNoDependenciesResManagerTest {
|
|
54
|
+
|
|
55
|
+
MockedStatic<SomeHelper> someHelper;
|
|
56
|
+
private BeginOperationTransactionArgs beginArgs;
|
|
57
|
+
|
|
58
|
+
@Before
|
|
59
|
+
public void before() {
|
|
60
|
+
someHelper = mockStatic(SomeHelper.class);
|
|
61
|
+
|
|
62
|
+
// 构建测试单据数据
|
|
63
|
+
DynamicObject[] bills = getBills();
|
|
64
|
+
|
|
65
|
+
// Mock BeginOperationTransactionArgs
|
|
66
|
+
beginArgs = mock(BeginOperationTransactionArgs.class);
|
|
67
|
+
when(beginArgs.getDataEntities()).thenReturn(bills);
|
|
68
|
+
when(beginArgs.getOperationKey()).thenReturn("save");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@After
|
|
72
|
+
public void after() {
|
|
73
|
+
someHelper.close();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public void reset() {
|
|
77
|
+
someHelper.reset();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private DynamicObject[] getBills() {
|
|
81
|
+
DynamicObject bill = new DynamicObjectMocker()
|
|
82
|
+
.add("id", 1L)
|
|
83
|
+
.add("billno", "TEST-001")
|
|
84
|
+
.add("billstatus", "A")
|
|
85
|
+
.getObject();
|
|
86
|
+
return new DynamicObject[]{bill};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@UnittestCaseInfo(
|
|
90
|
+
author = "<name> <<name>@kingdee.com>",
|
|
91
|
+
title = "Test begin operation transaction success",
|
|
92
|
+
targetClass = "kd.<包路径>.opplugin.modulename.XxxOp",
|
|
93
|
+
targetMethod = "beginoperationtransaction",
|
|
94
|
+
lastUpdateTime = "yyyy-MM-dd HH:mm:ss",
|
|
95
|
+
lastUpdateAuthor = "<name> <<name>@kingdee.com>",
|
|
96
|
+
methodSignature = "public void beginOperationTransaction(BeginOperationTransactionArgs e)",
|
|
97
|
+
testPoints = {"Functionality"},
|
|
98
|
+
description = "Test begin operation transaction success"
|
|
99
|
+
)
|
|
100
|
+
@Test
|
|
101
|
+
@DisplayName("测试事务开始:成功场景")
|
|
102
|
+
public void testBeginOperationTransaction_Success() {
|
|
103
|
+
//step 创建操作插件实例
|
|
104
|
+
OperationResult operation = mock(OperationResult.class);
|
|
105
|
+
when(operation.isSuccess()).thenReturn(true);
|
|
106
|
+
|
|
107
|
+
XxxOp op = new XxxOp() {
|
|
108
|
+
@Override
|
|
109
|
+
public OperationResult getOperationResult() {
|
|
110
|
+
return operation;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
OperateOption option = OperateOption.create();
|
|
114
|
+
op.setContext(null, null, option);
|
|
115
|
+
|
|
116
|
+
//step 执行方法
|
|
117
|
+
op.beginOperationTransaction(beginArgs);
|
|
118
|
+
|
|
119
|
+
//assert 验证预期行为
|
|
120
|
+
someHelper.verify(() -> SomeHelper.someMethod(any()), times(1));
|
|
121
|
+
reset();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 使用 OpBaseTest 基类方式
|
|
127
|
+
|
|
128
|
+
```java
|
|
129
|
+
public class XxxOpTest extends OpBaseTest {
|
|
130
|
+
|
|
131
|
+
MockedStatic<SomeHelper> someHelper;
|
|
132
|
+
|
|
133
|
+
@Override
|
|
134
|
+
@Before
|
|
135
|
+
public void before() {
|
|
136
|
+
super.before(); // 初始化 MainEntityType
|
|
137
|
+
someHelper = mockStatic(SomeHelper.class);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Override
|
|
141
|
+
@After
|
|
142
|
+
public void after() {
|
|
143
|
+
super.after();
|
|
144
|
+
someHelper.close();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@Override
|
|
148
|
+
protected AbstractOperationServicePlugIn buildOpPlugin() {
|
|
149
|
+
return new XxxOp();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Override
|
|
153
|
+
protected DynamicObject[] buildData() {
|
|
154
|
+
return new DynamicObject[]{
|
|
155
|
+
new DynamicObjectMocker()
|
|
156
|
+
.add("id", 1L)
|
|
157
|
+
.add("billno", "TEST-001")
|
|
158
|
+
.getObject()
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@Test
|
|
163
|
+
public void testPlugin() {
|
|
164
|
+
//step 注册操作插件(自动调用 onPreparePropertys 和 onAddValidators)
|
|
165
|
+
XxxOp op = registerOpPlugin();
|
|
166
|
+
|
|
167
|
+
//step 测试事务方法
|
|
168
|
+
BeginOperationTransactionArgs args = createBeginOperationTransactionArgs("save");
|
|
169
|
+
op.beginOperationTransaction(args);
|
|
170
|
+
|
|
171
|
+
//assert 验证结果
|
|
172
|
+
...
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## OpBaseTest 提供的工具方法
|
|
178
|
+
|
|
179
|
+
| 方法 | 返回值 | 说明 |
|
|
180
|
+
|------|--------|------|
|
|
181
|
+
| `registerOpPlugin()` | T (插件实例) | 注册并初始化操作插件 |
|
|
182
|
+
| `createBeforeOperationArgs(opKey)` | BeforeOperationArgs | 创建事务前参数 |
|
|
183
|
+
| `createBeginOperationTransactionArgs(opKey)` | BeginOperationTransactionArgs | 创建事务开始参数 |
|
|
184
|
+
| `createEndOperationTransactionArgs(opKey)` | EndOperationTransactionArgs | 创建事务结束参数 |
|
|
185
|
+
| `createAfterOperationArgs(opKey)` | AfterOperationArgs | 创建事务后参数 |
|
|
186
|
+
| `getValidators()` | List | 获取注册的校验器列表 |
|
|
187
|
+
| `getFieldKeys()` | List | 获取准备的字段键列表 |
|
|
188
|
+
|
|
189
|
+
## 测试重写 getOperationResult 的技巧
|
|
190
|
+
|
|
191
|
+
操作插件中经常需要根据 `getOperationResult()` 结果做不同处理,
|
|
192
|
+
用匿名子类方式注入 Mock 的 OperationResult:
|
|
193
|
+
|
|
194
|
+
```java
|
|
195
|
+
XxxOp op = new XxxOp() {
|
|
196
|
+
@Override
|
|
197
|
+
public OperationResult getOperationResult() {
|
|
198
|
+
OperationResult result = mock(OperationResult.class);
|
|
199
|
+
when(result.isSuccess()).thenReturn(true);
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## try-with-resources 模式
|
|
206
|
+
|
|
207
|
+
对于只在单个测试方法内使用的 MockedStatic,推荐 try-with-resources:
|
|
208
|
+
|
|
209
|
+
```java
|
|
210
|
+
@Test
|
|
211
|
+
public void testMethod() {
|
|
212
|
+
try (MockedStatic<OperationHelper> operationHelper = mockStatic(OperationHelper.class);
|
|
213
|
+
MockedStatic<ProcessPlanHelper> processPlanHelper = mockStatic(ProcessPlanHelper.class)) {
|
|
214
|
+
|
|
215
|
+
//step 配置 Mock
|
|
216
|
+
operationHelper.when(() -> OperationHelper.validator(any())).thenReturn(resultMap);
|
|
217
|
+
|
|
218
|
+
//step 执行
|
|
219
|
+
op.beforeExecuteOperationTransaction(args);
|
|
220
|
+
|
|
221
|
+
//assert 验证
|
|
222
|
+
assertTrue(condition);
|
|
223
|
+
}
|
|
224
|
+
// MockedStatic 自动关闭
|
|
225
|
+
}
|
|
226
|
+
```
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Validator 校验器测试模式
|
|
2
|
+
|
|
3
|
+
## 适用范围
|
|
4
|
+
|
|
5
|
+
`<模块组>-opplugin` 工程中 `Validator` 目录下的所有校验器类,
|
|
6
|
+
即继承 `AbstractValidator` 的类。
|
|
7
|
+
|
|
8
|
+
## 测试文件位置
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
<模块组>-opplugin/src/test/java/kd/<包路径>/opplugin/validator/XxxValidatorTest.java
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 核心思路
|
|
15
|
+
|
|
16
|
+
校验器的 `validate()` 方法在数据不满足条件时会调用 `addMessage()` / `addErrorMessage()` 等方法。
|
|
17
|
+
测试时通过**匿名子类**重写这些方法,将错误消息收集到 List 中,然后断言错误数量和内容。
|
|
18
|
+
|
|
19
|
+
## 基类选择
|
|
20
|
+
|
|
21
|
+
| 场景 | 基类 |
|
|
22
|
+
|------|------|
|
|
23
|
+
| 简单校验器(少量依赖) | 直接继承 `AbstractJunitNoDependenciesResManagerTest` |
|
|
24
|
+
| 需要通用校验器框架 | 继承 `ValidatorBaseTest`(提供 registerValidator 方法) |
|
|
25
|
+
|
|
26
|
+
## 模板结构(直接方式,推荐)
|
|
27
|
+
|
|
28
|
+
```java
|
|
29
|
+
package kd.<包路径>.opplugin.validator;
|
|
30
|
+
|
|
31
|
+
import kd.bos.dataentity.entity.DynamicObject;
|
|
32
|
+
import kd.bos.entity.ExtendedDataEntity;
|
|
33
|
+
import kd.bos.entity.validate.ErrorLevel;
|
|
34
|
+
import kd.bos.form.unittest.DisplayName;
|
|
35
|
+
import kd.bos.test.ext.annotaions.UnittestCaseInfo;
|
|
36
|
+
import kd.bos.unittest.AbstractJunitNoDependenciesResManagerTest;
|
|
37
|
+
import kd.bos.unittest.mock.DynamicObjectMocker;
|
|
38
|
+
import org.junit.After;
|
|
39
|
+
import org.junit.Before;
|
|
40
|
+
import org.junit.Test;
|
|
41
|
+
import org.mockito.MockedStatic;
|
|
42
|
+
import org.mockito.Mockito;
|
|
43
|
+
|
|
44
|
+
import java.math.BigDecimal;
|
|
45
|
+
import java.util.ArrayList;
|
|
46
|
+
import java.util.List;
|
|
47
|
+
|
|
48
|
+
import static org.mockito.ArgumentMatchers.*;
|
|
49
|
+
|
|
50
|
+
public class XxxValidatorTest extends AbstractJunitNoDependenciesResManagerTest {
|
|
51
|
+
|
|
52
|
+
// 1. Mock 依赖的静态类
|
|
53
|
+
private MockedStatic<SomeHelper> someHelper;
|
|
54
|
+
|
|
55
|
+
// 2. 错误消息收集器
|
|
56
|
+
private final List<String> errorMessages = new ArrayList<>();
|
|
57
|
+
private final List<Integer> errorIndexs = new ArrayList<>();
|
|
58
|
+
|
|
59
|
+
@Before
|
|
60
|
+
public void before() {
|
|
61
|
+
someHelper = Mockito.mockStatic(SomeHelper.class);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@After
|
|
65
|
+
public void after() {
|
|
66
|
+
someHelper.close();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 3. 构建校验器(匿名子类重写消息收集方法)
|
|
70
|
+
private XxxValidator buildValidator() {
|
|
71
|
+
return new XxxValidator() {
|
|
72
|
+
@Override
|
|
73
|
+
protected void addMessage(ExtendedDataEntity dataEntity, String content) {
|
|
74
|
+
errorIndexs.add(dataEntity.getDataEntityIndex());
|
|
75
|
+
errorMessages.add(content);
|
|
76
|
+
}
|
|
77
|
+
@Override
|
|
78
|
+
protected void addMessage(ExtendedDataEntity dataEntity, String content, ErrorLevel errorLevel) {
|
|
79
|
+
errorIndexs.add(dataEntity.getDataEntityIndex());
|
|
80
|
+
errorMessages.add(content);
|
|
81
|
+
}
|
|
82
|
+
@Override
|
|
83
|
+
protected void addErrorMessage(ExtendedDataEntity dataEntity, String content) {
|
|
84
|
+
errorIndexs.add(dataEntity.getDataEntityIndex());
|
|
85
|
+
errorMessages.add(content);
|
|
86
|
+
}
|
|
87
|
+
@Override
|
|
88
|
+
protected void addWarningMessage(ExtendedDataEntity dataEntity, String content) {
|
|
89
|
+
errorIndexs.add(dataEntity.getDataEntityIndex());
|
|
90
|
+
errorMessages.add(content);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 4. 构建测试数据
|
|
96
|
+
private ExtendedDataEntity[] buildValidatorData() {
|
|
97
|
+
DynamicObject bill = new DynamicObjectMocker()
|
|
98
|
+
.add("id", 1L)
|
|
99
|
+
.add("billstatus", "A")
|
|
100
|
+
.add("entryentity", new DynamicObjectMocker()
|
|
101
|
+
.add("id", 10L)
|
|
102
|
+
.add("qty", BigDecimal.TEN)
|
|
103
|
+
.getCollection())
|
|
104
|
+
.getObject();
|
|
105
|
+
|
|
106
|
+
ExtendedDataEntity entity = new ExtendedDataEntity(bill, 0, 0);
|
|
107
|
+
return new ExtendedDataEntity[]{entity};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 5. 测试方法
|
|
111
|
+
@UnittestCaseInfo(
|
|
112
|
+
author = "<name> <<name>@kingdee.com>",
|
|
113
|
+
title = "Test validate should pass",
|
|
114
|
+
targetClass = "kd.<包路径>.opplugin.Validator.XxxValidator",
|
|
115
|
+
targetMethod = "validate",
|
|
116
|
+
lastUpdateTime = "yyyy-MM-dd HH:mm:ss",
|
|
117
|
+
lastUpdateAuthor = "<name> <<name>@kingdee.com>",
|
|
118
|
+
methodSignature = "public void validate()",
|
|
119
|
+
testPoints = {"Functionality"},
|
|
120
|
+
description = "Test validate should pass"
|
|
121
|
+
)
|
|
122
|
+
@Test
|
|
123
|
+
@DisplayName("校验通过:数据满足所有条件")
|
|
124
|
+
public void testValidate_ShouldPass() {
|
|
125
|
+
//step 配置 Mock 返回值
|
|
126
|
+
someHelper.when(() -> SomeHelper.getData(any())).thenReturn(validData);
|
|
127
|
+
|
|
128
|
+
//step 构建校验器和数据
|
|
129
|
+
ExtendedDataEntity[] dataEntities = buildValidatorData();
|
|
130
|
+
XxxValidator validator = buildValidator();
|
|
131
|
+
validator.setDataEntities(dataEntities);
|
|
132
|
+
|
|
133
|
+
//step 执行校验
|
|
134
|
+
validator.validate();
|
|
135
|
+
|
|
136
|
+
//assert 无错误消息
|
|
137
|
+
assertEquals(0, errorMessages.size());
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Test
|
|
141
|
+
@DisplayName("校验失败:数据不满足条件")
|
|
142
|
+
public void testValidate_ShouldFail() {
|
|
143
|
+
//step 配置 Mock 返回值(构造不满足条件的数据环境)
|
|
144
|
+
someHelper.when(() -> SomeHelper.getData(any())).thenReturn(invalidData);
|
|
145
|
+
|
|
146
|
+
//step 构建校验器和数据
|
|
147
|
+
ExtendedDataEntity[] dataEntities = buildValidatorData();
|
|
148
|
+
XxxValidator validator = buildValidator();
|
|
149
|
+
validator.setDataEntities(dataEntities);
|
|
150
|
+
|
|
151
|
+
//step 执行校验
|
|
152
|
+
validator.validate();
|
|
153
|
+
|
|
154
|
+
//assert 应有错误消息
|
|
155
|
+
assertTrue(errorMessages.size() > 0);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 使用 ValidatorBaseTest 基类方式
|
|
161
|
+
|
|
162
|
+
**两种方式的选择:**
|
|
163
|
+
- **直接匿名子类**(上方模板):适用于大多数场景,提供最大灵活性,可自由控制 Mock 和数据构造
|
|
164
|
+
- **继承 ValidatorBaseTest**(本节):适用于校验逻辑简单、仅需验证“是否报错”的场景,提供 `registerValidator()` / `getValidatorErrorMessages()` 等便捷方法,省去手动构建 ExtendedDataEntity 的样板代码
|
|
165
|
+
|
|
166
|
+
当校验器测试逻辑较为简单且数据构造模式可复用时,推荐继承 `ValidatorBaseTest`:
|
|
167
|
+
|
|
168
|
+
```java
|
|
169
|
+
public class XxxValidatorTest extends ValidatorBaseTest {
|
|
170
|
+
|
|
171
|
+
@Override
|
|
172
|
+
protected AbstractValidator buildValidator() {
|
|
173
|
+
return new XxxValidator() {
|
|
174
|
+
@Override
|
|
175
|
+
protected void addErrorMessage(ExtendedDataEntity dataEntity, String content) {
|
|
176
|
+
addValidatorErrorMessage(content); // 基类提供的收集方法
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@Override
|
|
182
|
+
protected DynamicObject[] buildData() {
|
|
183
|
+
return new DynamicObject[]{
|
|
184
|
+
new DynamicObjectMocker()
|
|
185
|
+
.add("id", 1L)
|
|
186
|
+
.add("status", "A")
|
|
187
|
+
.getObject()
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@Test
|
|
192
|
+
public void testValidate() {
|
|
193
|
+
AbstractValidator validator = registerValidator("submit", "提交");
|
|
194
|
+
validator.validate();
|
|
195
|
+
assertEquals(0, getValidatorErrorMessages().size());
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## 关键要点
|
|
201
|
+
|
|
202
|
+
1. **必须重写消息方法**:校验器的 `addMessage` / `addErrorMessage` 等方法是 protected 的,
|
|
203
|
+
必须通过匿名子类重写才能捕获校验结果
|
|
204
|
+
2. **ExtendedDataEntity 包装**:校验器接收 `ExtendedDataEntity[]`,需要用 `new ExtendedDataEntity(dynamicObject, rowIndex, 0)` 包装
|
|
205
|
+
3. **设置 entityKey**:部分校验器依赖 `getEntityKey()` 判断单据类型,需要调用 `validator.setEntityKey(formId)`
|
|
206
|
+
4. **设置 operateKey**:部分校验器依赖操作类型,需调用 `validator.setOperateKey("submit")` 等
|