abapgit-agent 1.15.0 → 1.15.2
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/abap/CLAUDE.md +50 -51
- package/abap/guidelines/abapgit-fugr.md +1 -1
- package/abap/guidelines/abapgit-xml-only.md +1 -1
- package/abap/guidelines/abapgit.md +1 -1
- package/abap/guidelines/abaplint-local.md +1 -1
- package/abap/guidelines/abaplint.md +1 -1
- package/abap/guidelines/branch-workflow.md +1 -1
- package/abap/guidelines/cds-testing.md +233 -11
- package/abap/guidelines/cds.md +1 -1
- package/abap/guidelines/classes.md +1 -1
- package/abap/guidelines/comments.md +249 -0
- package/abap/guidelines/common-errors.md +1 -1
- package/abap/guidelines/debug-dump.md +1 -1
- package/abap/guidelines/debug-session.md +1 -1
- package/abap/guidelines/index.md +1 -0
- package/abap/guidelines/json.md +1 -1
- package/abap/guidelines/object-creation.md +18 -15
- package/abap/guidelines/objects.md +1 -1
- package/abap/guidelines/testing.md +4 -202
- package/abap/guidelines/unit-testable-code.md +1 -1
- package/abap/guidelines/workflow-detailed.md +1 -1
- package/package.json +2 -1
- package/src/commands/debug.js +40 -7
package/abap/CLAUDE.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
|
-
title:
|
|
3
|
+
title: AI Agent Instructions
|
|
4
4
|
nav_order: 1
|
|
5
5
|
parent: ABAP Development
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# AI Agent Instructions
|
|
9
9
|
|
|
10
10
|
This file provides guidelines for **generating ABAP code** in abapGit repositories.
|
|
11
11
|
|
|
@@ -157,10 +157,11 @@ Anything else (SAP namespace object, or SAP-delivered package)?
|
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
> **Tip for project setup**: Add package rules to `objects.local.md` so Claude never
|
|
160
|
-
> needs to ask.
|
|
160
|
+
> needs to ask. Run: `abapgit-agent ref --topic objects`
|
|
161
161
|
|
|
162
162
|
→ For exact XML templates: `abapgit-agent ref --topic abapgit` (CLAS/INTF/PROG/DDLS/DCLS/FUGR) or `abapgit-agent ref --topic abapgit-xml-only` (TABL/STRU/DTEL/TTYP/DOMA/MSAG)
|
|
163
163
|
→ For local helper/test-double class files: `abapgit-agent ref --topic object-creation`
|
|
164
|
+
→ For documentation comment format (shorttext, @parameter, CDS): `abapgit-agent ref --topic comments`
|
|
164
165
|
|
|
165
166
|
---
|
|
166
167
|
|
|
@@ -213,9 +214,15 @@ abapgit-agent pull --files src/<intf_name>.intf.abap,src/<class_name>.clas.abap
|
|
|
213
214
|
|
|
214
215
|
### 5. Local Helper / Test-Double Classes
|
|
215
216
|
|
|
216
|
-
|
|
217
|
+
When a class needs local helper classes or test doubles, create separate include files alongside the main class file:
|
|
217
218
|
|
|
218
|
-
|
|
219
|
+
```
|
|
220
|
+
<name>.clas.locals_def.abap ← local type/class definitions
|
|
221
|
+
<name>.clas.locals_imp.abap ← local class implementations
|
|
222
|
+
<name>.clas.testclasses.abap ← unit test classes (FOR TESTING)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**XML metadata when adding these files:**
|
|
219
226
|
|
|
220
227
|
```
|
|
221
228
|
Adding .clas.testclasses.abap to an existing class?
|
|
@@ -228,6 +235,7 @@ Adding .clas.locals_def.abap (local type definitions)?
|
|
|
228
235
|
<CLSCCINCL>X</CLSCCINCL>
|
|
229
236
|
```
|
|
230
237
|
|
|
238
|
+
→ For exact XML flag placement and test double class patterns: `abapgit-agent ref --topic object-creation`
|
|
231
239
|
→ For exact XML flag placement: `abapgit-agent ref --topic abapgit` (search "WITH_UNIT_TESTS")
|
|
232
240
|
|
|
233
241
|
---
|
|
@@ -330,8 +338,11 @@ AI thought process:
|
|
|
330
338
|
|
|
331
339
|
### 7. CDS Unit Tests
|
|
332
340
|
|
|
333
|
-
|
|
334
|
-
|
|
341
|
+
If a class under test reads a CDS view, use `CL_CDS_TEST_ENVIRONMENT` to provide test data — do **not** mock the database layer manually. Without this, test data setup is unreliable and tests may pass locally but fail on other systems.
|
|
342
|
+
|
|
343
|
+
**Trigger**: your class calls `SELECT FROM <cds_view>` directly or via a helper.
|
|
344
|
+
|
|
345
|
+
→ For full setup pattern and test double configuration: `abapgit-agent ref --topic cds-testing`
|
|
335
346
|
|
|
336
347
|
---
|
|
337
348
|
|
|
@@ -369,10 +380,7 @@ DATA(lo_cut) = NEW zcl_my_class( io_agent = lo_agent ).
|
|
|
369
380
|
DATA(ls_actual) = lo_cut->execute( ).
|
|
370
381
|
```
|
|
371
382
|
|
|
372
|
-
→
|
|
373
|
-
`abapgit-agent ref --topic unit-testable-code`
|
|
374
|
-
|
|
375
|
-
→ For class design rules (constructor injection, interfaces for dependencies):
|
|
383
|
+
→ For full API reference (EXPORT params, exceptions, inherited methods, common mistakes) and class design rules (constructor injection, interfaces for dependencies):
|
|
376
384
|
`abapgit-agent ref --topic unit-testable-code`
|
|
377
385
|
|
|
378
386
|
#### Running tests — use `unit` command
|
|
@@ -435,7 +443,7 @@ Never assume — wait for the user's answer before proceeding.
|
|
|
435
443
|
| HTTP 500 / runtime crash (ST22) | `dump` | Error already occurred |
|
|
436
444
|
| Wrong output, no crash | `debug` | Need to trace logic |
|
|
437
445
|
|
|
438
|
-
→
|
|
446
|
+
→ `abapgit-agent ref --topic debug-dump`
|
|
439
447
|
|
|
440
448
|
**Critical rules for `debug` sessions:**
|
|
441
449
|
|
|
@@ -496,7 +504,7 @@ abapgit-agent debug vars --json
|
|
|
496
504
|
abapgit-agent debug step --type continue --json # 4. release
|
|
497
505
|
```
|
|
498
506
|
|
|
499
|
-
→
|
|
507
|
+
→ `abapgit-agent ref --topic debug-session`
|
|
500
508
|
|
|
501
509
|
---
|
|
502
510
|
|
|
@@ -546,7 +554,7 @@ Projects can add their own abaplint notes to `guidelines/abaplint-local.md` in t
|
|
|
546
554
|
project repository. After running `abapgit-agent ref export`, the `ref` command
|
|
547
555
|
surfaces both bundled and project-specific guidance together.
|
|
548
556
|
|
|
549
|
-
→
|
|
557
|
+
→ `abapgit-agent ref --topic abaplint`
|
|
550
558
|
|
|
551
559
|
---
|
|
552
560
|
|
|
@@ -600,7 +608,7 @@ git push origin feature/my-change --force-with-lease
|
|
|
600
608
|
abapgit-agent pull --files src/<name>.clas.abap --sync-xml
|
|
601
609
|
```
|
|
602
610
|
|
|
603
|
-
→
|
|
611
|
+
→ `abapgit-agent ref --topic branch-workflow`
|
|
604
612
|
|
|
605
613
|
### Trunk Workflow (`"mode": "trunk"`)
|
|
606
614
|
|
|
@@ -716,47 +724,38 @@ Modified ABAP files?
|
|
|
716
724
|
```
|
|
717
725
|
|
|
718
726
|
→ For creating new objects (what files to write): `abapgit-agent ref --topic object-creation`
|
|
719
|
-
→
|
|
727
|
+
→ For full workflow decision tree and error indicators: `abapgit-agent ref --topic workflow-detailed`
|
|
720
728
|
|
|
721
729
|
---
|
|
722
730
|
|
|
723
731
|
## Guidelines Index
|
|
724
732
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
|
735
|
-
|
|
736
|
-
| `
|
|
737
|
-
| `
|
|
738
|
-
| `
|
|
739
|
-
| `
|
|
740
|
-
| `
|
|
741
|
-
| `
|
|
742
|
-
| `
|
|
743
|
-
| `
|
|
744
|
-
| `
|
|
745
|
-
| `
|
|
746
|
-
| `
|
|
747
|
-
| `
|
|
748
|
-
| `
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
| `guidelines/run-probe-classes.md` | run Command — AI Guidelines (probe classes, scratchWorkspace) |
|
|
752
|
-
| `guidelines/probe-poc.md` | Probe and PoC — Full Decision Flow |
|
|
753
|
-
| `guidelines/branch-workflow.md` | Branch Workflow |
|
|
754
|
-
| `guidelines/workflow-detailed.md` | Development Workflow (Detailed) |
|
|
755
|
-
| `guidelines/object-creation.md` | Object Creation (XML metadata, local classes) |
|
|
756
|
-
| `guidelines/cds-testing.md` | CDS Testing (Test Double Framework) |
|
|
757
|
-
| `guidelines/abaplint.md` | abaplint Rule Guidelines (prefer_inline trap, safe patterns) |
|
|
758
|
-
|
|
759
|
-
These guidelines are automatically searched by the `ref` command.
|
|
733
|
+
| Command | Topic |
|
|
734
|
+
|---------|-------|
|
|
735
|
+
| `ref --topic sql` | ABAP SQL Best Practices |
|
|
736
|
+
| `ref --topic exceptions` | Exception Handling |
|
|
737
|
+
| `ref --topic classes` | ABAP Classes and Objects |
|
|
738
|
+
| `ref --topic objects` | Object Naming Conventions (defaults) |
|
|
739
|
+
| `ref --topic comments` | Documentation Comments (ABAP DOC, shorttext, @parameter, CDS `//`, program `*&---`) |
|
|
740
|
+
| `ref --topic testing` | Unit Testing |
|
|
741
|
+
| `ref --topic unit-testable-code` | Unit Testable Code (Dependency Injection) |
|
|
742
|
+
| `ref --topic cds` | CDS Views |
|
|
743
|
+
| `ref --topic cds-testing` | CDS Testing (Test Double Framework) |
|
|
744
|
+
| `ref --topic json` | JSON Handling |
|
|
745
|
+
| `ref --topic common-errors` | Common ABAP Errors - Quick Fixes |
|
|
746
|
+
| `ref --topic abapgit` | abapGit XML Metadata — **use for CDS/DDLS XML**, also CLAS, INTF, PROG, DCLS, FUGR |
|
|
747
|
+
| `ref --topic abapgit-xml-only` | abapGit XML Metadata — XML-only objects (TABL, STRU, DTEL, TTYP, DOMA, MSAG) |
|
|
748
|
+
| `ref --topic abapgit-fugr` | abapGit XML Metadata — Function Group (FUGR) details |
|
|
749
|
+
| `ref --topic abaplint` | abaplint Rule Guidelines (prefer_inline trap, safe patterns) |
|
|
750
|
+
| `ref --topic debug-session` | Debug Session Guide |
|
|
751
|
+
| `ref --topic debug-dump` | Dump Analysis Guide |
|
|
752
|
+
| `ref --topic branch-workflow` | Branch Workflow |
|
|
753
|
+
| `ref --topic workflow-detailed` | Development Workflow (Detailed) |
|
|
754
|
+
| `ref --topic object-creation` | Object Creation (XML metadata, local classes) |
|
|
755
|
+
| `ref --topic run-probe-classes` | run Command — AI Guidelines (probe classes, scratchWorkspace) |
|
|
756
|
+
| `ref --topic probe-poc` | Probe and PoC — Full Decision Flow |
|
|
757
|
+
|
|
758
|
+
> `objects.local.md` — **Project** Naming Conventions (created by `init`, never overwritten). Read directly from `guidelines/objects.local.md` — no ref topic.
|
|
760
759
|
|
|
761
760
|
---
|
|
762
761
|
|
|
@@ -1,28 +1,250 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
3
|
title: CDS Testing
|
|
4
|
-
nav_order:
|
|
4
|
+
nav_order: 10
|
|
5
5
|
parent: ABAP Coding Guidelines
|
|
6
6
|
grand_parent: ABAP Development
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# CDS Testing
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**Searchable keywords**: CDS test double, CL_CDS_TEST_ENVIRONMENT, IF_CDS_TEST_ENVIRONMENT, insert_test_data, clear_doubles, create_for_multiple_cds, class_setup, class_teardown, aggregation test, CDS unit test
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## TOPICS IN THIS FILE
|
|
14
|
+
1. When to Use CDS Test Doubles - line 22
|
|
15
|
+
2. Basic Setup Pattern - line 30
|
|
16
|
+
3. Testing CDS Views with Aggregations - line 100
|
|
17
|
+
4. Testing CDS Views that Select from Another CDS View - line 130
|
|
18
|
+
5. Key Classes and Methods - line 170
|
|
19
|
+
6. Important Usage Notes - line 190
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. When to Use CDS Test Doubles
|
|
14
24
|
|
|
15
25
|
```
|
|
16
26
|
❌ WRONG: Use regular AUnit test class without test doubles
|
|
17
27
|
✅ CORRECT: Use CL_CDS_TEST_ENVIRONMENT to create test doubles for CDS views
|
|
18
28
|
```
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
30
|
+
Use the CDS Test Double Framework (`CL_CDS_TEST_ENVIRONMENT`) whenever:
|
|
31
|
+
- A class under test calls `SELECT FROM <cds_view>` directly or via a helper
|
|
32
|
+
- You need controlled test data (not production data)
|
|
33
|
+
- You need to test CDS view logic with specific scenarios
|
|
34
|
+
|
|
35
|
+
**Why**: CDS views read from database tables. Using test doubles allows injecting test data
|
|
36
|
+
without affecting production data, and keeps tests fast and isolated.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 2. Basic Setup Pattern
|
|
41
|
+
|
|
42
|
+
The test class lives in `<classname>.clas.testclasses.abap`. The CDS view itself has no
|
|
43
|
+
`.testclasses` file — test it through a regular ABAP class that reads it.
|
|
44
|
+
|
|
45
|
+
```abap
|
|
46
|
+
"-------------------------
|
|
47
|
+
" CLASS DEFINITION
|
|
48
|
+
"-------------------------
|
|
49
|
+
CLASS ltcl_cds_test DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FINAL.
|
|
50
|
+
|
|
51
|
+
PRIVATE SECTION.
|
|
52
|
+
" IMPORTANT: Use interface type, not class type!
|
|
53
|
+
DATA mo_cds_env TYPE REF TO if_cds_test_environment.
|
|
54
|
+
|
|
55
|
+
" IMPORTANT: class_setup/teardown must be CLASS-METHODS (static)!
|
|
56
|
+
CLASS-DATA mo_cds_env_static TYPE REF TO if_cds_test_environment.
|
|
57
|
+
|
|
58
|
+
METHODS setup.
|
|
59
|
+
METHODS test_cds_with_doubles FOR TESTING.
|
|
60
|
+
|
|
61
|
+
CLASS-METHODS: class_setup,
|
|
62
|
+
class_teardown.
|
|
63
|
+
|
|
64
|
+
ENDCLASS.
|
|
65
|
+
|
|
66
|
+
"-------------------------
|
|
67
|
+
" CLASS IMPLEMENTATION
|
|
68
|
+
"-------------------------
|
|
69
|
+
CLASS ltcl_cds_test IMPLEMENTATION.
|
|
70
|
+
|
|
71
|
+
METHOD class_setup.
|
|
72
|
+
" Create CDS test environment — framework auto-creates doubles for dependencies
|
|
73
|
+
mo_cds_env_static = cl_cds_test_environment=>create(
|
|
74
|
+
i_for_entity = 'ZC_MY_CDS_VIEW' ).
|
|
75
|
+
ENDMETHOD.
|
|
76
|
+
|
|
77
|
+
METHOD class_teardown.
|
|
78
|
+
" Clean up test environment
|
|
79
|
+
mo_cds_env_static->destroy( ).
|
|
80
|
+
ENDMETHOD.
|
|
81
|
+
|
|
82
|
+
METHOD setup.
|
|
83
|
+
" IMPORTANT: Assign static env to instance and clear doubles before each test
|
|
84
|
+
mo_cds_env = mo_cds_env_static.
|
|
85
|
+
mo_cds_env->clear_doubles( ).
|
|
86
|
+
ENDMETHOD.
|
|
87
|
+
|
|
88
|
+
METHOD test_cds_with_doubles.
|
|
89
|
+
" IMPORTANT: Must declare table type first — cannot inline in VALUE #()!
|
|
90
|
+
DATA lt_test_data TYPE TABLE OF zc_my_cds_view WITH EMPTY KEY.
|
|
91
|
+
lt_test_data = VALUE #(
|
|
92
|
+
( field1 = 'A' field2 = 100 )
|
|
93
|
+
( field1 = 'B' field2 = 200 ) ).
|
|
94
|
+
|
|
95
|
+
" Insert test data using named parameter
|
|
96
|
+
mo_cds_env->insert_test_data( i_data = lt_test_data ).
|
|
97
|
+
|
|
98
|
+
" Select from CDS view
|
|
99
|
+
SELECT * FROM zc_my_cds_view INTO TABLE @DATA(lt_result).
|
|
100
|
+
|
|
101
|
+
" Verify results
|
|
102
|
+
cl_abap_unit_assert=>assert_not_initial(
|
|
103
|
+
act = lt_result
|
|
104
|
+
msg = 'Result should not be empty' ).
|
|
105
|
+
|
|
106
|
+
cl_abap_unit_assert=>assert_equals(
|
|
107
|
+
act = lines( lt_result )
|
|
108
|
+
exp = 2
|
|
109
|
+
msg = 'Expected 2 rows' ).
|
|
110
|
+
ENDMETHOD.
|
|
111
|
+
|
|
112
|
+
ENDCLASS.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 3. Testing CDS Views with Aggregations (SUM, COUNT, GROUP BY)
|
|
118
|
+
|
|
119
|
+
For CDS views with aggregations, insert test data into the **base tables** (SFLIGHT, SCARR,
|
|
120
|
+
SBOOK, etc.), not directly into the CDS view. The framework routes the inserts through the
|
|
121
|
+
aggregation pipeline.
|
|
122
|
+
|
|
123
|
+
```abap
|
|
124
|
+
METHOD test_aggregation.
|
|
125
|
+
" Insert data into base tables via CDS test doubles
|
|
126
|
+
DATA lt_scarr TYPE TABLE OF scarr WITH EMPTY KEY.
|
|
127
|
+
lt_scarr = VALUE #( ( carrid = 'LH' carrname = 'Lufthansa' currcode = 'EUR' ) ).
|
|
128
|
+
mo_cds_env->insert_test_data( i_data = lt_scarr ).
|
|
129
|
+
|
|
130
|
+
DATA lt_sflight TYPE TABLE OF sflight WITH EMPTY KEY.
|
|
131
|
+
lt_sflight = VALUE #( ( carrid = 'LH' connid = '0400' fldate = '20240115'
|
|
132
|
+
seatsmax = 200 seatsocc = 100 ) ).
|
|
133
|
+
mo_cds_env->insert_test_data( i_data = lt_sflight ).
|
|
134
|
+
|
|
135
|
+
DATA lt_sbook TYPE TABLE OF sbook WITH EMPTY KEY.
|
|
136
|
+
lt_sbook = VALUE #(
|
|
137
|
+
( carrid = 'LH' connid = '0400' fldate = '20240115' bookid = '0001' forcuram = 1000 )
|
|
138
|
+
( carrid = 'LH' connid = '0400' fldate = '20240115' bookid = '0002' forcuram = 2000 )
|
|
139
|
+
( carrid = 'LH' connid = '0400' fldate = '20240115' bookid = '0003' forcuram = 3000 ) ).
|
|
140
|
+
mo_cds_env->insert_test_data( i_data = lt_sbook ).
|
|
141
|
+
|
|
142
|
+
" Select from CDS view — aggregations will use test double data
|
|
143
|
+
SELECT * FROM zc_flight_revenue INTO TABLE @DATA(lt_result).
|
|
144
|
+
|
|
145
|
+
cl_abap_unit_assert=>assert_equals(
|
|
146
|
+
exp = 3
|
|
147
|
+
act = lt_result[ 1 ]-numberofbookings
|
|
148
|
+
msg = 'Should have 3 bookings' ).
|
|
149
|
+
|
|
150
|
+
cl_abap_unit_assert=>assert_equals(
|
|
151
|
+
exp = '6000.00'
|
|
152
|
+
act = lt_result[ 1 ]-totalrevenue
|
|
153
|
+
msg = 'Total revenue should be 6000.00' ).
|
|
154
|
+
ENDMETHOD.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 4. Testing CDS Views that Select from Another CDS View
|
|
160
|
+
|
|
161
|
+
> **Note:** This pattern applies when your design **already has** a CDS view that selects from
|
|
162
|
+
> another CDS view. It does NOT mean you should split a single view into two — use a single
|
|
163
|
+
> CDS view with GROUP BY / JOIN when the business logic fits.
|
|
164
|
+
|
|
165
|
+
When your CDS view selects from **another CDS view** (not a base table), `create` raises
|
|
166
|
+
`CX_CDS_FAILURE`. Use `create_for_multiple_cds` instead and list all CDS entities in the
|
|
167
|
+
dependency chain.
|
|
168
|
+
|
|
169
|
+
```abap
|
|
170
|
+
METHOD class_setup.
|
|
171
|
+
" ZC_TopView selects from ZC_IntermediateView (another CDS view entity)
|
|
172
|
+
" → must use create_for_multiple_cds and list all CDS entities
|
|
173
|
+
mo_cds_env_static = cl_cds_test_environment=>create_for_multiple_cds(
|
|
174
|
+
i_for_entities = VALUE #(
|
|
175
|
+
( 'ZC_TOPVIEW' ) " the view under test
|
|
176
|
+
( 'ZC_INTERMEDIATEVIEW' ) " the CDS view it selects from
|
|
177
|
+
) ).
|
|
178
|
+
ENDMETHOD.
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Insert test data into the **intermediate CDS view** (not the base tables), because that is
|
|
182
|
+
what the top-level view reads:
|
|
183
|
+
|
|
184
|
+
```abap
|
|
185
|
+
METHOD test_read.
|
|
186
|
+
DATA lt_source TYPE TABLE OF zc_intermediateview WITH EMPTY KEY.
|
|
187
|
+
lt_source = VALUE #(
|
|
188
|
+
( field1 = 'A' field2 = 100 )
|
|
189
|
+
( field1 = 'B' field2 = 200 ) ).
|
|
190
|
+
mo_cds_env->insert_test_data( i_data = lt_source ).
|
|
191
|
+
|
|
192
|
+
SELECT * FROM zc_topview INTO TABLE @DATA(lt_result).
|
|
193
|
+
|
|
194
|
+
cl_abap_unit_assert=>assert_equals(
|
|
195
|
+
exp = 2 act = lines( lt_result ) msg = 'Expected 2 rows' ).
|
|
196
|
+
ENDMETHOD.
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Rules:**
|
|
200
|
+
- List the view under test **and all CDS views it depends on** in `i_for_entities`
|
|
201
|
+
- Insert data into the **direct source** of the top-level view (the intermediate CDS view)
|
|
202
|
+
- Order in `i_for_entities` does not matter
|
|
203
|
+
- If `create` raises `CX_CDS_FAILURE`, switch to `create_for_multiple_cds`
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 5. Key Classes and Methods
|
|
208
|
+
|
|
209
|
+
| Item | Type / Usage |
|
|
210
|
+
|------|-------------|
|
|
211
|
+
| `CL_CDS_TEST_ENVIRONMENT` | Class — entry point, use its `CREATE` / `CREATE_FOR_MULTIPLE_CDS` methods |
|
|
212
|
+
| `IF_CDS_TEST_ENVIRONMENT` | Interface — declare your variable with this type |
|
|
213
|
+
| `CLASS-METHODS class_setup` | Must be static (`CLASS-METHODS`, not `METHODS`) |
|
|
214
|
+
| `CL_ABAP_UNIT_ASSERT` | Standard AUnit assertion class |
|
|
215
|
+
|
|
216
|
+
| Method | Purpose |
|
|
217
|
+
|--------|---------|
|
|
218
|
+
| `CL_CDS_TEST_ENVIRONMENT=>create( i_for_entity = ... )` | Environment for a CDS view over base tables |
|
|
219
|
+
| `CL_CDS_TEST_ENVIRONMENT=>create_for_multiple_cds( i_for_entities = ... )` | Environment when CDS view selects from another CDS view |
|
|
220
|
+
| `insert_test_data( i_data = ... )` | Inject test rows into a test double |
|
|
221
|
+
| `clear_doubles( )` | Remove all test rows — call in `setup` before each test |
|
|
222
|
+
| `destroy( )` | Tear down the environment — call in `class_teardown` |
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## 6. Important Usage Notes
|
|
227
|
+
|
|
228
|
+
1. **Declare with interface type**: `DATA mo_cds_env TYPE REF TO if_cds_test_environment` —
|
|
229
|
+
`create` returns an interface reference, not a class reference.
|
|
230
|
+
|
|
231
|
+
2. **Static lifecycle methods**: `class_setup` and `class_teardown` must be `CLASS-METHODS`
|
|
232
|
+
(not `METHODS`). The environment is expensive to create — build it once per class run.
|
|
233
|
+
|
|
234
|
+
3. **Table type before VALUE #()**: Must declare `DATA lt_tab TYPE TABLE OF <type> WITH EMPTY KEY`
|
|
235
|
+
before using `VALUE #()` — inline declaration inside `VALUE #()` is not supported here.
|
|
236
|
+
|
|
237
|
+
4. **Auto-created dependencies**: When the CDS view selects only from base tables, the framework
|
|
238
|
+
auto-creates test doubles — do not specify an `i_dependency_list`. When the CDS view selects
|
|
239
|
+
from another CDS view, use `create_for_multiple_cds` (see section 4).
|
|
240
|
+
|
|
241
|
+
5. **Aggregations**: For CDS views with SUM/COUNT/GROUP BY, insert test data into the base tables
|
|
242
|
+
(SFLIGHT, SCARR, etc.), not the CDS view itself.
|
|
243
|
+
|
|
244
|
+
6. **Clear doubles**: Always call `clear_doubles` in `setup` to ensure test isolation.
|
|
245
|
+
|
|
246
|
+
7. **Enable associations**: Set `test_associations = 'X'` in `create` only when explicitly
|
|
247
|
+
testing CDS association navigation.
|
|
24
248
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- Testing CDS views with aggregations (insert into base tables)
|
|
28
|
-
- Testing CDS views that select from another CDS view (`create_for_multiple_cds`)
|
|
249
|
+
8. **Exception handling**: Declare test methods with `RAISING cx_static_check` if the code
|
|
250
|
+
under test raises checked exceptions.
|
package/abap/guidelines/cds.md
CHANGED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Documentation Comments
|
|
4
|
+
nav_order: 6
|
|
5
|
+
parent: ABAP Coding Guidelines
|
|
6
|
+
grand_parent: ABAP Development
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ABAP Documentation Comments
|
|
10
|
+
|
|
11
|
+
**Searchable keywords**: ABAP DOC, "! comment, shorttext synchronized, @parameter, @raising, inline comment, CDS comment, // comment, program header, *&---, documentation comment
|
|
12
|
+
|
|
13
|
+
## TOPICS IN THIS FILE
|
|
14
|
+
1. Two Types of Comments - line 18
|
|
15
|
+
2. OO Class Documentation (CLAS) - line 37
|
|
16
|
+
3. Interface Documentation (INTF) - line 101
|
|
17
|
+
4. Program Header (PROG) - line 120
|
|
18
|
+
5. CDS View Comments (DDLS) - line 137
|
|
19
|
+
6. When NOT to Comment - line 161
|
|
20
|
+
7. Quick Decision Table - line 182
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Two Types of Comments
|
|
25
|
+
|
|
26
|
+
ABAP has two distinct comment styles with different purposes:
|
|
27
|
+
|
|
28
|
+
**`"!` (ABAP DOC)** — parsed by the ABAP system; surfaced in ADT and SE24 as F2 help.
|
|
29
|
+
Use for API-level documentation: class declarations, interface declarations, and method signatures.
|
|
30
|
+
This is the equivalent of Javadoc. The system reads it — it is metadata, not decoration.
|
|
31
|
+
|
|
32
|
+
**`"!` is only valid immediately before these statements:**
|
|
33
|
+
`CLASS ... DEFINITION`, `INTERFACE`, `METHODS`, `CLASS-METHODS`, `EVENTS`, `DATA`, `CLASS-DATA`, `CONSTANTS`.
|
|
34
|
+
|
|
35
|
+
**`"!` before `TYPES` is a syntax error** — the Code Inspector reports "ABAP Doc comment is in the wrong position."
|
|
36
|
+
Use a regular `"` comment to describe type structures instead.
|
|
37
|
+
|
|
38
|
+
**`"` (regular inline comment)** — source-only; not parsed by the ABAP system.
|
|
39
|
+
Use inside method bodies to explain non-obvious logic, and to label `TYPES` blocks.
|
|
40
|
+
Never use `"!` inside method implementations — it has no effect there.
|
|
41
|
+
|
|
42
|
+
**Default behaviour (when to add automatically vs. only on request):**
|
|
43
|
+
|
|
44
|
+
| Comment Type | Default Behaviour |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `"! <p class="shorttext synchronized">` on CLASS/INTF | **Always auto-add** — synced to XML `<DESCRIPT>`; it IS object metadata |
|
|
47
|
+
| `@EndUserText.label` on CDS view | **Always auto-add** — same: it IS the CDS description |
|
|
48
|
+
| `"!` + `@parameter` on INTF public methods | **Always auto-add** — interfaces are the contract; self-documenting |
|
|
49
|
+
| `"!` + `@parameter` on CLAS public methods | **Auto-add** when creating a new class from scratch |
|
|
50
|
+
| `"!` on existing CLAS private/protected methods | **Only if requested** — adding to existing code is refactoring |
|
|
51
|
+
| `"` inline comments inside method bodies | **Only when non-obvious** — never add redundant comments |
|
|
52
|
+
| `*&---` program header | **Always auto-add** — standard for every PROG |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 2. OO Class Documentation (CLAS)
|
|
57
|
+
|
|
58
|
+
### Class-level shorttext
|
|
59
|
+
|
|
60
|
+
Place the `"!` shorttext immediately before `CLASS ... DEFINITION`:
|
|
61
|
+
|
|
62
|
+
```abap
|
|
63
|
+
"! <p class="shorttext synchronized">Pull ABAP objects from a remote git repository</p>
|
|
64
|
+
CLASS zcl_abgagt_agent DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This shorttext is **synced to the XML `<DESCRIPT>` field** by abapGit. Creating a class without
|
|
68
|
+
it means the XML description is blank and the object cannot be searched by description in SE24/SE80.
|
|
69
|
+
**Always add it** when creating any new class.
|
|
70
|
+
|
|
71
|
+
**Note on `*"*` auto-header:** When a class has local definitions or inherits from another class,
|
|
72
|
+
abapGit writes a 3-line `*"*"` block at the top of the `.clas.abap` file:
|
|
73
|
+
|
|
74
|
+
```abap
|
|
75
|
+
*"* use this source file for the definition and implementation of
|
|
76
|
+
*"* local helper classes, interface definitions and type
|
|
77
|
+
*"* temporary on-the-fly implementation
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
In that case, the `"!` shorttext goes on line 4 (after the block), not line 1.
|
|
81
|
+
|
|
82
|
+
### Method documentation
|
|
83
|
+
|
|
84
|
+
Place `"!` documentation in the DEFINITION section, PUBLIC SECTION only, immediately before the
|
|
85
|
+
`METHODS` statement:
|
|
86
|
+
|
|
87
|
+
```abap
|
|
88
|
+
"! Pull files from remote and activate them
|
|
89
|
+
"! @parameter it_files | List of files to activate (relative paths)
|
|
90
|
+
"! @parameter rv_success | True if all objects activated successfully
|
|
91
|
+
"! @raising zcx_abapgit_exception | If pull or activation fails
|
|
92
|
+
METHODS pull_files
|
|
93
|
+
IMPORTING it_files TYPE string_table
|
|
94
|
+
RETURNING VALUE(rv_success) TYPE abap_bool
|
|
95
|
+
RAISING zcx_abapgit_exception.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Rules:**
|
|
99
|
+
|
|
100
|
+
- **ALWAYS add** `"! <p class="shorttext synchronized">` when creating any new CLASS — no exceptions.
|
|
101
|
+
- **ALWAYS add** `"!` + `@parameter` for all PUBLIC methods when creating a new class from scratch.
|
|
102
|
+
- When **modifying existing** code, only add or update comments if explicitly asked.
|
|
103
|
+
- `@parameter <name> | <description>` — one line per IMPORTING/EXPORTING/RETURNING/CHANGING param.
|
|
104
|
+
- `@raising <exception> | <reason>` — one line per RAISING exception.
|
|
105
|
+
- PRIVATE and PROTECTED methods: optional; add only if the purpose is non-obvious.
|
|
106
|
+
- Place ONLY in the DEFINITION section — do NOT repeat `"!` in the IMPLEMENTATION section.
|
|
107
|
+
|
|
108
|
+
**Full example:**
|
|
109
|
+
|
|
110
|
+
```abap
|
|
111
|
+
"! <p class="shorttext synchronized">Syntax checker for ABAP class source</p>
|
|
112
|
+
CLASS zcl_abgagt_syntax_chk_clas DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
|
113
|
+
PUBLIC SECTION.
|
|
114
|
+
INTERFACES zif_abgagt_syntax_checker.
|
|
115
|
+
|
|
116
|
+
"! Create a new syntax checker instance
|
|
117
|
+
"! @parameter ii_agent | Agent used for HTTP communication
|
|
118
|
+
CLASS-METHODS create
|
|
119
|
+
IMPORTING ii_agent TYPE REF TO zif_abgagt_agent
|
|
120
|
+
RETURNING VALUE(ro_check) TYPE REF TO zif_abgagt_syntax_checker.
|
|
121
|
+
|
|
122
|
+
PRIVATE SECTION.
|
|
123
|
+
DATA mi_agent TYPE REF TO zif_abgagt_agent.
|
|
124
|
+
ENDCLASS.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 3. Interface Documentation (INTF)
|
|
130
|
+
|
|
131
|
+
Interfaces follow the same `"!` pattern as classes. The shorttext goes immediately before
|
|
132
|
+
`INTERFACE ... PUBLIC`, and every method gets `"!` + `@parameter`.
|
|
133
|
+
|
|
134
|
+
```abap
|
|
135
|
+
"! <p class="shorttext synchronized">Contract for all syntax checker implementations</p>
|
|
136
|
+
INTERFACE zif_abgagt_syntax_checker PUBLIC.
|
|
137
|
+
|
|
138
|
+
"! Check the syntax of ABAP source code
|
|
139
|
+
"! @parameter iv_source | Full ABAP source text to check
|
|
140
|
+
"! @parameter rv_result | JSON-encoded result with errors list
|
|
141
|
+
"! @raising zcx_abapgit_exception | On communication failure
|
|
142
|
+
METHODS check_syntax
|
|
143
|
+
IMPORTING iv_source TYPE string
|
|
144
|
+
RETURNING VALUE(rv_result) TYPE string
|
|
145
|
+
RAISING zcx_abapgit_exception.
|
|
146
|
+
|
|
147
|
+
ENDINTERFACE.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Canonical example:** `zif_abgagt_syntax_checker.intf.abap` in this repository.
|
|
151
|
+
|
|
152
|
+
**Rules:**
|
|
153
|
+
- **ALWAYS add** `"! <p class="shorttext synchronized">` for every new interface.
|
|
154
|
+
- **ALWAYS add** `"!` + `@parameter` for every method — interfaces are the published contract.
|
|
155
|
+
- No exceptions for "obvious" method names — document them anyway.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 4. Program Header (PROG)
|
|
160
|
+
|
|
161
|
+
Programs use the traditional `*&---` block, NOT ABAP DOC. Place it before the `REPORT` statement:
|
|
162
|
+
|
|
163
|
+
```abap
|
|
164
|
+
*&---------------------------------------------------------------------*
|
|
165
|
+
*& Report Z_MY_PROGRAM
|
|
166
|
+
*&---------------------------------------------------------------------*
|
|
167
|
+
*& Brief description of what the program does
|
|
168
|
+
*&---------------------------------------------------------------------*
|
|
169
|
+
REPORT z_my_program.
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Rules:**
|
|
173
|
+
- **ALWAYS add** this header block when creating any new program.
|
|
174
|
+
- Do NOT add `"! <p class="shorttext synchronized">` before `REPORT` — it is not parsed there.
|
|
175
|
+
- Inline comments inside `START-OF-SELECTION` and other events use the regular `"` style.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 5. CDS View Comments (DDLS)
|
|
180
|
+
|
|
181
|
+
CDS DDL source (`.ddls.asddls`) uses a completely different comment syntax from ABAP:
|
|
182
|
+
|
|
183
|
+
- Line comment: `// text`
|
|
184
|
+
- Block comment: `/* text */`
|
|
185
|
+
|
|
186
|
+
**Never use ABAP-style `"` inside CDS source** — it causes a CDS syntax error.
|
|
187
|
+
|
|
188
|
+
### `@EndUserText.label` — the CDS equivalent of shorttext
|
|
189
|
+
|
|
190
|
+
This annotation IS the object description. Always include it before the view entity definition:
|
|
191
|
+
|
|
192
|
+
```cds
|
|
193
|
+
@EndUserText.label: 'Revenue summary by carrier'
|
|
194
|
+
define view entity ZC_FlightRevenue
|
|
195
|
+
as select from sflight
|
|
196
|
+
{
|
|
197
|
+
key carrid, // IATA carrier code
|
|
198
|
+
key connid, // Connection number
|
|
199
|
+
sum( price ) as TotalRevenue
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Rules:**
|
|
204
|
+
- **ALWAYS add** `@EndUserText.label` when creating any new CDS view entity.
|
|
205
|
+
- Add `// text` line comments after non-obvious field names.
|
|
206
|
+
- Use `/* ... */` for multi-line block comments if needed (e.g., explaining a complex join).
|
|
207
|
+
- No `"!` or `"` comments anywhere in `.ddls.asddls` files.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 6. When NOT to Comment
|
|
212
|
+
|
|
213
|
+
The guiding principle: **comments explain *why*, not *what***.
|
|
214
|
+
|
|
215
|
+
```abap
|
|
216
|
+
" BAD — repeats the code, adds no value
|
|
217
|
+
ls_params-mode = 'abort'. " Set mode to abort
|
|
218
|
+
|
|
219
|
+
" GOOD — explains a non-obvious rule
|
|
220
|
+
ls_params-mode = 'abort'. " INITIAL means abort (caller omitting the field = abort semantics)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Do NOT add comments when:**
|
|
224
|
+
- The comment would just restate the variable name or method call.
|
|
225
|
+
- A private helper method simply delegates to an interface method (the name is self-explanatory).
|
|
226
|
+
- `@parameter` description would be identical to the parameter name (e.g., `"! @parameter iv_name | Name`).
|
|
227
|
+
- You are modifying existing code and the task did not ask for documentation changes.
|
|
228
|
+
|
|
229
|
+
**DO add comments when:**
|
|
230
|
+
- The logic involves a non-obvious ABAP-specific behaviour or workaround.
|
|
231
|
+
- There is a caller precondition that cannot be expressed in the method signature.
|
|
232
|
+
- A COND/SWITCH mapping table needs to explain the business rule behind each branch.
|
|
233
|
+
- A loop exit or CONTINUE has a non-obvious condition.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 7. Quick Decision Table
|
|
238
|
+
|
|
239
|
+
| Object | Where | Style | Required? |
|
|
240
|
+
|--------|-------|-------|-----------|
|
|
241
|
+
| CLAS (global) | Before `CLASS DEFINITION` | `"! <p class="shorttext synchronized">` | **Always** |
|
|
242
|
+
| CLAS public method | Before `METHODS` in DEFINITION | `"! desc` + `"! @parameter` | Yes (new objects) |
|
|
243
|
+
| CLAS private/protected method | Before `METHODS` in DEFINITION | `"! desc` | Only if non-obvious |
|
|
244
|
+
| INTF method | Before `METHODS` in interface | `"! desc` + `"! @parameter` | **Always** |
|
|
245
|
+
| PROG | First lines before `REPORT` | `*&---` block | **Always** |
|
|
246
|
+
| DDLS view entity | Before `define view entity` | `@EndUserText.label` annotation | **Always** |
|
|
247
|
+
| DDLS field | After field name | `// text` | For non-obvious fields |
|
|
248
|
+
| Method body | Inside `METHOD...ENDMETHOD` | `" text` | Only for non-obvious logic |
|
|
249
|
+
| TYPES block | Before `TYPES:` in interface/class | `" text` | Optional — `"!` is invalid here |
|
package/abap/guidelines/index.md
CHANGED
|
@@ -31,6 +31,7 @@ This folder contains detailed ABAP coding guidelines that can be searched using
|
|
|
31
31
|
| `object-creation.md` | Object Creation (XML metadata, local classes) |
|
|
32
32
|
| `cds-testing.md` | CDS Testing (Test Double Framework) |
|
|
33
33
|
| `abaplint.md` | abaplint Rule Guidelines (prefer_inline trap, safe patterns) |
|
|
34
|
+
| `comments.md` | Documentation Comments (ABAP DOC, shorttext, @parameter, CDS //) |
|
|
34
35
|
|
|
35
36
|
## Usage
|
|
36
37
|
|
package/abap/guidelines/json.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
3
|
title: Object Creation
|
|
4
|
-
nav_order:
|
|
4
|
+
nav_order: 22
|
|
5
5
|
parent: ABAP Coding Guidelines
|
|
6
6
|
grand_parent: ABAP Development
|
|
7
7
|
---
|
|
@@ -17,26 +17,29 @@ Replace `<name>` with the actual object name from this project's naming conventi
|
|
|
17
17
|
|
|
18
18
|
| Object Type | ABAP Source File | XML File | Details |
|
|
19
19
|
|-------------|-----------------|----------|---------|
|
|
20
|
-
| Class | `<name>.clas.abap` | `<name>.clas.xml` |
|
|
21
|
-
| Interface | `<name>.intf.abap` | `<name>.intf.xml` |
|
|
22
|
-
| Program | `<name>.prog.abap` | `<name>.prog.xml` |
|
|
23
|
-
|
|
|
24
|
-
| CDS
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
| Class (CLAS) | `<name>.clas.abap` | `<name>.clas.xml` | `ref --topic abapgit` |
|
|
21
|
+
| Interface (INTF) | `<name>.intf.abap` | `<name>.intf.xml` | `ref --topic abapgit` |
|
|
22
|
+
| Program (PROG) | `<name>.prog.abap` | `<name>.prog.xml` | `ref --topic abapgit` |
|
|
23
|
+
| CDS View Entity (DDLS) | `<name>.ddls.asddls` | `<name>.ddls.xml` | **Use by default** — `ref --topic abapgit` |
|
|
24
|
+
| CDS Access Control (DCLS) | `<name>.dcls.asdcls` | `<name>.dcls.xml` | `ref --topic abapgit` |
|
|
25
|
+
| Function Group (FUGR) | `<name>.fugr.abap` + includes | `<name>.fugr.xml` | `ref --topic abapgit` |
|
|
26
|
+
| Table (TABL) | *(none — XML-only)* | `<name>.tabl.xml` | `ref --topic abapgit-xml-only` |
|
|
27
|
+
| Structure (STRU) | *(none — XML-only)* | `<name>.stru.xml` | `ref --topic abapgit-xml-only` |
|
|
28
|
+
| Data Element (DTEL) | *(none — XML-only)* | `<name>.dtel.xml` | `ref --topic abapgit-xml-only` |
|
|
29
|
+
| Table Type (TTYP) | *(none — XML-only)* | `<name>.ttyp.xml` | `ref --topic abapgit-xml-only` |
|
|
30
|
+
| Domain (DOMA) | *(none — XML-only)* | `<name>.doma.xml` | `ref --topic abapgit-xml-only` |
|
|
31
|
+
| Message Class (MSAG) | *(none — XML-only)* | `<name>.msag.xml` | `ref --topic abapgit-xml-only` |
|
|
32
|
+
|
|
33
|
+
> **XML-only objects (TABL, STRU, DTEL, TTYP, DOMA, MSAG)**: create only the `.xml` file — there is no `.abap` source file.
|
|
31
34
|
> After committing and pushing, pull with: `pull --files <folder>/<name>.tabl.xml --sync-xml`
|
|
32
35
|
|
|
33
|
-
**IMPORTANT: When user says "create CDS view", create CDS View Entity by default.**
|
|
36
|
+
**IMPORTANT: When user says "create CDS view", create CDS View Entity (DDLS) by default.**
|
|
34
37
|
|
|
35
38
|
**Why:** Modern S/4HANA standard, simpler (no SQL view), no namespace conflicts.
|
|
36
39
|
|
|
37
40
|
**For complete XML templates, DDL examples, and detailed comparison:**
|
|
38
|
-
- **CDS Views**: `
|
|
39
|
-
- **XML
|
|
41
|
+
- **CDS Views + DCLS + FUGR**: `abapgit-agent ref --topic abapgit`
|
|
42
|
+
- **XML-only objects**: `abapgit-agent ref --topic abapgit-xml-only`
|
|
40
43
|
|
|
41
44
|
---
|
|
42
45
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
3
|
title: Unit Testing
|
|
4
|
-
nav_order:
|
|
4
|
+
nav_order: 7
|
|
5
5
|
parent: ABAP Coding Guidelines
|
|
6
6
|
grand_parent: ABAP Development
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Unit Testing
|
|
10
10
|
|
|
11
|
-
**Searchable keywords**: unit test, AUnit, test class, cl_abap_unit_assert, FOR TESTING, setup, teardown, RISK LEVEL, DURATION,
|
|
11
|
+
**Searchable keywords**: unit test, AUnit, test class, cl_abap_unit_assert, FOR TESTING, setup, teardown, RISK LEVEL, DURATION, WITH_UNIT_TESTS, testclasses
|
|
12
12
|
|
|
13
13
|
## TOPICS IN THIS FILE
|
|
14
14
|
1. Local Test Classes - line 22
|
|
@@ -16,8 +16,6 @@ grand_parent: ABAP Development
|
|
|
16
16
|
3. Required Elements - line 35
|
|
17
17
|
4. Naming Conventions - line 67
|
|
18
18
|
5. Common Mistake: DDLS Testing - line 133
|
|
19
|
-
6. CDS Test Doubles - line 163
|
|
20
|
-
7. CDS with Aggregations - line 247
|
|
21
19
|
|
|
22
20
|
## Unit Testing with Local Test Classes
|
|
23
21
|
|
|
@@ -169,206 +167,10 @@ Or via abapGit: Pull the files and run tests in the ABAP system.
|
|
|
169
167
|
|
|
170
168
|
## Unit Testing CDS Views
|
|
171
169
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### When to Use CDS Test Doubles
|
|
175
|
-
|
|
176
|
-
- Testing code that reads from CDS views
|
|
177
|
-
- Need controlled test data (not production data)
|
|
178
|
-
- Testing CDS view logic with specific scenarios
|
|
179
|
-
|
|
180
|
-
### CDS Test Double Framework
|
|
181
|
-
|
|
182
|
-
Use `CL_CDS_TEST_ENVIRONMENT` for controlled test data:
|
|
183
|
-
|
|
184
|
-
```abap
|
|
185
|
-
"-------------------------
|
|
186
|
-
" CLASS DEFINITION
|
|
187
|
-
"-------------------------
|
|
188
|
-
CLASS ltcl_cds_test DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FINAL.
|
|
189
|
-
|
|
190
|
-
PRIVATE SECTION.
|
|
191
|
-
" IMPORTANT: Use interface type, not class type!
|
|
192
|
-
DATA mo_cds_env TYPE REF TO if_cds_test_environment.
|
|
193
|
-
|
|
194
|
-
" IMPORTANT: class_setup/teardown must be CLASS-METHODS (static)!
|
|
195
|
-
CLASS-DATA mo_cds_env_static TYPE REF TO if_cds_test_environment.
|
|
196
|
-
|
|
197
|
-
METHODS setup.
|
|
198
|
-
METHODS test_cds_with_doubles FOR TESTING.
|
|
199
|
-
|
|
200
|
-
CLASS-METHODS: class_setup,
|
|
201
|
-
class_teardown.
|
|
202
|
-
|
|
203
|
-
ENDCLASS.
|
|
204
|
-
|
|
205
|
-
"-------------------------
|
|
206
|
-
" CLASS IMPLEMENTATION
|
|
207
|
-
"-------------------------
|
|
208
|
-
CLASS ltcl_cds_test IMPLEMENTATION.
|
|
209
|
-
|
|
210
|
-
METHOD class_setup.
|
|
211
|
-
" Create CDS test environment - framework auto-creates doubles for dependencies
|
|
212
|
-
mo_cds_env_static = cl_cds_test_environment=>create(
|
|
213
|
-
i_for_entity = 'ZC_MY_CDS_VIEW' ).
|
|
214
|
-
ENDMETHOD.
|
|
215
|
-
|
|
216
|
-
METHOD class_teardown.
|
|
217
|
-
" Clean up test environment
|
|
218
|
-
mo_cds_env_static->destroy( ).
|
|
219
|
-
ENDMETHOD.
|
|
220
|
-
|
|
221
|
-
METHOD setup.
|
|
222
|
-
" IMPORTANT: Assign static env to instance and clear doubles
|
|
223
|
-
mo_cds_env = mo_cds_env_static.
|
|
224
|
-
mo_cds_env->clear_doubles( ).
|
|
225
|
-
ENDMETHOD.
|
|
226
|
-
|
|
227
|
-
METHOD test_cds_with_doubles.
|
|
228
|
-
" IMPORTANT: Must declare table type first, cannot inline in VALUE!
|
|
229
|
-
DATA lt_test_data TYPE TABLE OF zc_my_cds_view WITH EMPTY KEY.
|
|
230
|
-
lt_test_data = VALUE #(
|
|
231
|
-
( field1 = 'A' field2 = 100 )
|
|
232
|
-
( field1 = 'B' field2 = 200 ) ).
|
|
233
|
-
|
|
234
|
-
" Insert test data using named parameter
|
|
235
|
-
mo_cds_env->insert_test_data( i_data = lt_test_data ).
|
|
236
|
-
|
|
237
|
-
" Select from CDS view
|
|
238
|
-
SELECT * FROM zc_my_cds_view INTO TABLE @DATA(lt_result).
|
|
239
|
-
|
|
240
|
-
" Verify results
|
|
241
|
-
cl_abap_unit_assert=>assert_not_initial(
|
|
242
|
-
act = lt_result
|
|
243
|
-
msg = 'Result should not be empty' ).
|
|
244
|
-
|
|
245
|
-
cl_abap_unit_assert=>assert_equals(
|
|
246
|
-
act = lines( lt_result )
|
|
247
|
-
exp = 2
|
|
248
|
-
msg = 'Expected 2 rows' ).
|
|
249
|
-
ENDMETHOD.
|
|
250
|
-
|
|
251
|
-
ENDCLASS.
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### Testing CDS Views with Aggregations (SUM, COUNT, GROUP BY)
|
|
255
|
-
|
|
256
|
-
For CDS views with aggregations, insert test data into the **base tables** (SFLIGHT, SCARR, SBOOK), not directly into the CDS view:
|
|
257
|
-
|
|
258
|
-
```abap
|
|
259
|
-
METHOD test_aggregation.
|
|
260
|
-
" Insert data into base tables via CDS test doubles
|
|
261
|
-
DATA lt_scarr TYPE TABLE OF scarr WITH EMPTY KEY.
|
|
262
|
-
lt_scarr = VALUE #( ( carrid = 'LH' carrname = 'Lufthansa' currcode = 'EUR' ) ).
|
|
263
|
-
mo_cds_env->insert_test_data( i_data = lt_scarr ).
|
|
264
|
-
|
|
265
|
-
DATA lt_sflight TYPE TABLE OF sflight WITH EMPTY KEY.
|
|
266
|
-
lt_sflight = VALUE #( ( carrid = 'LH' connid = '0400' fldate = '20240115'
|
|
267
|
-
seatsmax = 200 seatsocc = 100 ) ).
|
|
268
|
-
mo_cds_env->insert_test_data( i_data = lt_sflight ).
|
|
269
|
-
|
|
270
|
-
DATA lt_sbook TYPE TABLE OF sbook WITH EMPTY KEY.
|
|
271
|
-
lt_sbook = VALUE #(
|
|
272
|
-
( carrid = 'LH' connid = '0400' fldate = '20240115' bookid = '0001' forcuram = 1000 )
|
|
273
|
-
( carrid = 'LH' connid = '0400' fldate = '20240115' bookid = '0002' forcuram = 2000 )
|
|
274
|
-
( carrid = 'LH' connid = '0400' fldate = '20240115' bookid = '0003' forcuram = 3000 ) ).
|
|
275
|
-
mo_cds_env->insert_test_data( i_data = lt_sbook ).
|
|
276
|
-
|
|
277
|
-
" Select from CDS view - aggregations will use test double data
|
|
278
|
-
SELECT * FROM zc_flight_revenue INTO TABLE @DATA(lt_result).
|
|
279
|
-
|
|
280
|
-
" Verify aggregations
|
|
281
|
-
cl_abap_unit_assert=>assert_equals(
|
|
282
|
-
exp = 3
|
|
283
|
-
act = lt_result[ 1 ]-numberofbookings
|
|
284
|
-
msg = 'Should have 3 bookings' ).
|
|
285
|
-
|
|
286
|
-
cl_abap_unit_assert=>assert_equals(
|
|
287
|
-
exp = '6000.00'
|
|
288
|
-
act = lt_result[ 1 ]-totalrevenue
|
|
289
|
-
msg = 'Total revenue should be 6000.00' ).
|
|
290
|
-
ENDMETHOD.
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### Testing CDS Views that Select from Another CDS View
|
|
294
|
-
|
|
295
|
-
> **Note:** This pattern applies when your design **already has** a CDS view that selects from another CDS view. It does NOT mean you should split a single view into two — use a single CDS view with GROUP BY / JOIN when the business logic fits.
|
|
296
|
-
|
|
297
|
-
When your CDS view selects from **another CDS view** (not a base table), `create` will raise `CX_CDS_FAILURE`. Use `create_for_multiple_cds` instead and list all CDS entities in the dependency chain.
|
|
298
|
-
|
|
299
|
-
```abap
|
|
300
|
-
METHOD class_setup.
|
|
301
|
-
" ZC_TopView selects from ZC_IntermediateView (another CDS view entity)
|
|
302
|
-
" → must use create_for_multiple_cds and list all CDS entities
|
|
303
|
-
mo_cds_env_static = cl_cds_test_environment=>create_for_multiple_cds(
|
|
304
|
-
i_for_entities = VALUE #(
|
|
305
|
-
( 'ZC_TOPVIEW' ) " the view under test
|
|
306
|
-
( 'ZC_INTERMEDIATEVIEW' ) " the CDS view it selects from
|
|
307
|
-
) ).
|
|
308
|
-
ENDMETHOD.
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
Insert test data into the intermediate CDS view (not the base tables), since that is what the top-level view reads:
|
|
312
|
-
|
|
313
|
-
```abap
|
|
314
|
-
METHOD test_read.
|
|
315
|
-
DATA lt_source TYPE TABLE OF zc_intermediateview WITH EMPTY KEY.
|
|
316
|
-
lt_source = VALUE #(
|
|
317
|
-
( field1 = 'A' field2 = 100 )
|
|
318
|
-
( field1 = 'B' field2 = 200 ) ).
|
|
319
|
-
mo_cds_env->insert_test_data( i_data = lt_source ).
|
|
320
|
-
|
|
321
|
-
SELECT * FROM zc_topview INTO TABLE @DATA(lt_result).
|
|
322
|
-
|
|
323
|
-
cl_abap_unit_assert=>assert_equals(
|
|
324
|
-
exp = 2 act = lines( lt_result ) msg = 'Expected 2 rows' ).
|
|
325
|
-
ENDMETHOD.
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
**Rules:**
|
|
329
|
-
- List the view under test **and all CDS views it depends on** in `i_for_entities`
|
|
330
|
-
- Insert data into the **direct source** of the top-level view (the intermediate CDS view)
|
|
331
|
-
- Order in `i_for_entities` does not matter
|
|
332
|
-
- If you get `CX_CDS_FAILURE` when using `create`, switch to `create_for_multiple_cds`
|
|
333
|
-
|
|
334
|
-
---
|
|
335
|
-
|
|
336
|
-
### Key Classes for CDS Testing
|
|
337
|
-
|
|
338
|
-
| Item | Type/Usage |
|
|
339
|
-
|------|------------|
|
|
340
|
-
| `CL_CDS_TEST_ENVIRONMENT` | Class with `CREATE` method |
|
|
341
|
-
| `IF_CDS_TEST_ENVIRONMENT` | Interface (CREATE returns this type) |
|
|
342
|
-
| `CLASS-METHODS` | `class_setup` and `class_teardown` must be static methods |
|
|
343
|
-
| `CL_OSQL_TEST_ENVIRONMENT` | Test doubles for database tables (use for aggregations) |
|
|
344
|
-
| `CL_ABAP_UNIT_ASSERT` | Assertions |
|
|
345
|
-
|
|
346
|
-
### Key Methods
|
|
347
|
-
|
|
348
|
-
| Method | Purpose |
|
|
349
|
-
|--------|---------|
|
|
350
|
-
| `CL_CDS_TEST_ENVIRONMENT=>create( i_for_entity = ... )` | Create test environment for a CDS view over base tables |
|
|
351
|
-
| `CL_CDS_TEST_ENVIRONMENT=>create_for_multiple_cds( i_for_entities = ... )` | Create test environment when the CDS view selects from another CDS view |
|
|
352
|
-
| `insert_test_data( i_data = ... )` | Insert test data into test doubles |
|
|
353
|
-
| `clear_doubles` | Clear test data before each test method |
|
|
354
|
-
| `destroy` | Clean up after test class |
|
|
355
|
-
|
|
356
|
-
### Important Usage Notes
|
|
357
|
-
|
|
358
|
-
1. **Use interface type**: `DATA mo_cds_env TYPE REF TO if_cds_test_environment` - the CREATE method returns an interface reference
|
|
359
|
-
2. **CLASS-METHODS required**: `class_setup` and `class_teardown` must be declared with `CLASS-METHODS` (not `METHODS`)
|
|
360
|
-
3. **Table type declaration**: Must declare `DATA lt_tab TYPE TABLE OF <type> WITH EMPTY KEY` before using `VALUE #()`
|
|
361
|
-
4. **Auto-created dependencies**: When the CDS view selects only from **base tables**, the framework auto-creates test doubles — do not specify `i_dependency_list`. When the CDS view selects from **another CDS view**, use `create_for_multiple_cds` instead (see section below).
|
|
362
|
-
5. **Aggregations**: For CDS views with SUM/COUNT/GROUP BY, insert test data into base tables (SFLIGHT, SCARR, etc.)
|
|
363
|
-
6. **Clear doubles**: Call `clear_doubles` in `setup` method before each test
|
|
364
|
-
7. **Enable associations**: Set `test_associations = 'X'` only if testing CDS associations
|
|
365
|
-
8. **Exception handling**: Declare test methods with `RAISING cx_static_check` for proper exception handling
|
|
366
|
-
|
|
367
|
-
### Search Reference for More Details
|
|
170
|
+
For full CDS test double patterns (basic setup, aggregations, CDS-on-CDS), see:
|
|
368
171
|
|
|
369
172
|
```bash
|
|
370
|
-
abapgit-agent ref
|
|
371
|
-
abapgit-agent ref --topic unit-tests
|
|
173
|
+
abapgit-agent ref --topic cds-testing
|
|
372
174
|
```
|
|
373
175
|
|
|
374
176
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abapgit-agent",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.2",
|
|
4
4
|
"description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
|
|
5
5
|
"files": [
|
|
6
6
|
"bin/",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"test:cmd:tree": "node tests/run-all.js --cmd --command=tree",
|
|
38
38
|
"test:cmd:dump": "node tests/run-all.js --cmd --command=dump",
|
|
39
39
|
"test:cmd:debug": "node tests/run-all.js --cmd --command=debug",
|
|
40
|
+
"test:debug": "node tests/run-all.js --debug",
|
|
40
41
|
"test:debug:scenarios": "bash tests/integration/debug-scenarios.sh",
|
|
41
42
|
"test:debug:scenarios:1": "bash tests/integration/debug-scenarios.sh 1",
|
|
42
43
|
"test:debug:scenarios:2": "bash tests/integration/debug-scenarios.sh 2",
|
package/src/commands/debug.js
CHANGED
|
@@ -109,8 +109,23 @@ function objectUri(name, includeType) {
|
|
|
109
109
|
}
|
|
110
110
|
// FUGR source includes: L<group>U<NN>, L<group>TOP, L<group>F<NN>, etc.
|
|
111
111
|
// All start with 'L'. Customer Z/Y programs never start with 'L'.
|
|
112
|
+
// Correct ADT URI (verified against abap-adt-api):
|
|
113
|
+
// /sap/bc/adt/functions/groups/<group>/includes/<include>/source/main
|
|
114
|
+
// NOT /programs/includes/ — that path is for standalone PROG/I includes only.
|
|
115
|
+
// Group name is derived by stripping leading 'L' and trailing suffix:
|
|
116
|
+
// U<NN> — FM source include (U01, U02, ...)
|
|
117
|
+
// TOP — pool include
|
|
118
|
+
// F<NN> — form include
|
|
119
|
+
// XX — internal include
|
|
112
120
|
if (/^L/.test(upper)) {
|
|
113
|
-
|
|
121
|
+
const withoutL = upper.slice(1); // e.g. ZCAIS_DEMOU01
|
|
122
|
+
const group = withoutL
|
|
123
|
+
.replace(/U\d+$/, '') // strip Unn suffix
|
|
124
|
+
.replace(/TOP$/, '') // strip TOP suffix
|
|
125
|
+
.replace(/F\d+$/, '') // strip Fnn suffix
|
|
126
|
+
.replace(/XX$/, '') // strip XX suffix
|
|
127
|
+
.toLowerCase();
|
|
128
|
+
return `/sap/bc/adt/functions/groups/${group}/includes/${lower}/source/main`;
|
|
114
129
|
}
|
|
115
130
|
return `/sap/bc/adt/programs/programs/${lower}`;
|
|
116
131
|
}
|
|
@@ -236,15 +251,25 @@ async function refreshBreakpoints(config, adt, bps) {
|
|
|
236
251
|
|
|
237
252
|
const serverResults = parseBreakpointResponse(resp.body || '', bps);
|
|
238
253
|
|
|
239
|
-
// Match server results back to local bps by uri+line
|
|
254
|
+
// Match server results back to local bps by uri+line.
|
|
255
|
+
// ADT may return a different canonical URI than what was sent (e.g. it rewrites
|
|
256
|
+
// /functions/groups/<g>/includes/<inc>/... to /functions/groups/<g>/fmodules/<fm>/...).
|
|
257
|
+
// Fall back to matching by line alone when URI doesn't match, then adopt the
|
|
258
|
+
// server's canonical URI so future refreshes continue to work.
|
|
240
259
|
const valid = [];
|
|
241
260
|
const stale = [];
|
|
242
261
|
for (const bp of bps) {
|
|
243
|
-
|
|
262
|
+
let match = serverResults.find(r => r.uri === bp.uri && r.line === bp.line);
|
|
263
|
+
if (!match) {
|
|
264
|
+
// Fallback: match by line number alone (handles URI canonicalization by ADT)
|
|
265
|
+
match = serverResults.find(r => r.line === bp.line);
|
|
266
|
+
}
|
|
244
267
|
if (match && match.error) {
|
|
245
268
|
stale.push({ ...bp, error: match.error });
|
|
246
269
|
} else if (match && match.id) {
|
|
247
|
-
|
|
270
|
+
// Adopt the server's canonical URI so subsequent refreshes match correctly
|
|
271
|
+
const canonicalUri = match.uri || bp.uri;
|
|
272
|
+
valid.push({ ...bp, id: match.id, uri: canonicalUri });
|
|
248
273
|
} else {
|
|
249
274
|
// No match in response — server silently dropped it (e.g. expired)
|
|
250
275
|
stale.push({ ...bp, error: 'Not registered on server' });
|
|
@@ -398,9 +423,16 @@ async function cmdSet(args, config, adt) {
|
|
|
398
423
|
}
|
|
399
424
|
|
|
400
425
|
// Update local state with server-assigned IDs
|
|
426
|
+
// Use URI+line match first; fall back to line-only for FUGR where ADT rewrites
|
|
427
|
+
// /includes/<inc>/ to /fmodules/<fm>/ in the response.
|
|
401
428
|
const updatedWithServerIds = updated.map(bp => {
|
|
402
|
-
const sr = serverResults.find(r => r.uri === bp.uri && r.line === bp.line)
|
|
403
|
-
|
|
429
|
+
const sr = serverResults.find(r => r.uri === bp.uri && r.line === bp.line)
|
|
430
|
+
|| serverResults.find(r => r.line === bp.line);
|
|
431
|
+
if (sr && sr.id) {
|
|
432
|
+
const canonicalUri = sr.uri || bp.uri;
|
|
433
|
+
return { ...bp, id: sr.id, uri: canonicalUri };
|
|
434
|
+
}
|
|
435
|
+
return bp;
|
|
404
436
|
});
|
|
405
437
|
if (_saveBpState) _saveBpState(config, updatedWithServerIds);
|
|
406
438
|
|
|
@@ -435,7 +467,8 @@ async function cmdSet(args, config, adt) {
|
|
|
435
467
|
|
|
436
468
|
if (jsonOutput) {
|
|
437
469
|
const out = added.map(a => {
|
|
438
|
-
const sr = serverResults.find(r => r.uri === a.uri && r.line === a.line)
|
|
470
|
+
const sr = serverResults.find(r => r.uri === a.uri && r.line === a.line)
|
|
471
|
+
|| serverResults.find(r => r.line === a.line);
|
|
439
472
|
return { id: (sr && sr.id) || null, object: a.name, line: a.line };
|
|
440
473
|
});
|
|
441
474
|
console.log(JSON.stringify(out.length === 1 ? out[0] : out));
|