| name | generate-cds-unit-test |
| description | Generate an ABAP Unit test class for a CDS entity using the CDS Test Double Framework — replicates SAP Joule's "CDS Unit Test Generation" capability. Use when asked to "generate CDS unit tests", "test this CDS view", "create CDSTDF tests", or "write unit tests for ZI_SALESORDER". |
Generate CDS Unit Test
Generate an ABAP Unit test class for a CDS entity using the CDS Test Double Framework.
This skill replicates SAP Joule's "CDS Unit Test Generation" capability by combining ARC-1 (SAP system access) with mcp-sap-docs (documentation & best practices).
Smart Defaults (apply silently, do NOT ask)
| Setting | Default | Rationale |
|---|
| Test class name | ZCL_TEST_<entity_name> | Standard convention |
| Package | $TMP | Fast prototyping |
| Test scope | All testable semantics | User can narrow after seeing the list |
| Risk level | HARMLESS | CDS test doubles don't modify real data |
| Duration | SHORT | Unit tests should be fast |
Input
The user provides a CDS entity name (e.g., ZI_SALESORDER, ZC_TRAVEL).
Only the CDS entity name is required. If the user provides just an entity name, apply Smart Defaults and proceed immediately.
Optionally, the user may specify:
- Test class name (default:
ZCL_TEST_<entity_name>)
- Package (default:
$TMP)
- Transport request (only needed for non-
$TMP packages)
Step 1: Gather CDS Entity Context
Read the CDS entity, its field structure, and all dependencies in just 3 tool calls.
1a. Read the CDS DDL source
SAPRead(type="DDLS", name="<entity_name>")
1b. Get structured field list
SAPRead(type="DDLS", name="<entity_name>", include="elements")
Returns a formatted listing of all fields with key markers, aliases, association references, and expression types (calculated, case, cast, coalesce). Use this to understand the entity's structure without parsing raw DDL.
1c. Get dependency context (tables, views, associations)
SAPContext(type="DDLS", name="<entity_name>")
Automatically extracts all data sources (FROM, JOIN), associations, compositions, and projection bases from the CDS DDL. For each dependency, fetches the full source with type fallback (DDLS → TABL → STRU). This gives you:
- Underlying table definitions with field types (needed for correctly-typed test data)
- Other CDS view sources (needed to understand transitive logic)
- Association targets
For deeper dependency graphs (e.g., a consumption view → interface view → table), use depth=2:
SAPContext(type="DDLS", name="<entity_name>", depth=2)
1d. (Optional) Read metadata extensions and behavior definition
SAPRead(type="DDLX", name="<entity_name>")
SAPRead(type="BDEF", name="<entity_name>")
These may fail if no DDLX/BDEF exists — that's fine, skip them. Only needed if the entity has UI annotations or RAP behavior.
Step 2: Analyze CDS Semantics and Propose Test Cases
Analyze the CDS DDL source you read in Step 1 and identify all testable semantics. Group them by category:
Semantic Categories to Look For
| Category | What to Look For in DDL | Example Test Case |
|---|
| Calculations | Arithmetic expressions (field1 * field2, field1 - field2) | Insert known values, verify calculated result |
| CASE expressions | case when ... then ... else ... end | One test per branch |
| Type casts | cast(field as abap.dec(15,2)), cast(field as abap.char(10)) | Insert value, verify cast output |
| WHERE filters | where status = 'A' or where amount > 0 | Insert matching + non-matching rows, verify only matching appear |
| HAVING clauses | having sum(amount) > 1000 | Insert data that triggers and doesn't trigger the filter |
| JOINs | inner join, left outer join, association | Insert matching keys in both tables; test with non-matching keys for outer joins |
| Aggregations | sum(), count(), avg(), min(), max() with group by | Insert multiple rows, verify aggregated result |
| Null handling | coalesce(field, 'default'), null-safe expressions | Insert row with null field, verify default |
| Parameters | with parameters p_param : type | Call view with parameter, verify filtering |
| Currency/Unit conversion | currency_conversion(), unit_conversion() | Insert conversion data, verify result |
| String operations | concat(), substring(), length() | Insert string values, verify output |
| Associations | association [0..*] to ... | Insert data in both sides, verify navigation |
Output to User
Present the identified test cases as a numbered list:
Identified test cases for ZI_SALESORDER:
1. [CALCULATION] test_net_amount_calculation — Verify net_amount = gross_amount - discount
2. [CASE] test_status_text_open — Verify status 'O' maps to 'Open'
3. [CASE] test_status_text_closed — Verify status 'C' maps to 'Closed'
4. [JOIN] test_customer_join — Verify customer data is correctly joined
5. [WHERE] test_active_orders_filter — Verify only active orders are returned
6. [NULL] test_description_coalesce — Verify null description gets default value
Ask the user: "Which test cases should I generate? (all / specific numbers / skip any?)"
If the user says "all" or doesn't respond with preferences, generate all.
Step 3: Fetch CDS Test Double Framework Reference
Use mcp-sap-docs to get the latest CDS Test Double Framework patterns. This ensures generated code follows SAP best practices.
search("CDS test double framework cl_cds_test_environment")
Also fetch:
search("ABAP Unit cl_abap_unit_assert")
Use the returned documentation to inform the generated code patterns. Key things to verify:
- Correct
cl_cds_test_environment=>create() signature
- Correct
get_double() / insert() pattern
- Correct assertion method names
Step 4: Generate the Test Class
Generate a complete ABAP test class following this structure:
Template
"! @testing <CDS_ENTITY_NAME>
CLASS <test_class_name> DEFINITION
PUBLIC FINAL
FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PUBLIC SECTION.
PROTECTED SECTION.
PRIVATE SECTION.
CLASS-DATA environment TYPE REF TO if_cds_test_environment.
CLASS-METHODS class_setup RAISING cx_static_check.
CLASS-METHODS class_teardown.
METHODS setup.
"! <description of test case 1>
METHODS <test_method_1> FOR TESTING RAISING cx_static_check.
"! <description of test case 2>
METHODS <test_method_2> FOR TESTING RAISING cx_static_check.
" ... one method per selected test case
ENDCLASS.
CLASS <test_class_name> IMPLEMENTATION.
METHOD class_setup.
environment = cl_cds_test_environment=>create( i_for_entity = '<CDS_ENTITY_NAME>' ).
ENDMETHOD.
METHOD class_teardown.
environment->destroy( ).
ENDMETHOD.
METHOD setup.
environment->clear_doubles( ).
ENDMETHOD.
METHOD <test_method_1>.
" Arrange — prepare test data for underlying tables
DATA lt_<table> TYPE STANDARD TABLE OF <underlying_table> WITH EMPTY KEY.
lt_<table> = VALUE #(
( <field1> = '<value1>' <field2> = '<value2>' ... )
).
environment->get_double( '<UNDERLYING_TABLE>' )->insert( lt_<table> ).
" Act — SELECT from the CDS entity under test
SELECT * FROM <cds_entity> INTO TABLE @DATA(lt_result).
" Assert — verify expected behavior
cl_abap_unit_assert=>assert_equals(
act = lines( lt_result )
exp = <expected_count>
msg = |Expected { <expected_count> } rows| ).
cl_abap_unit_assert=>assert_equals(
act = lt_result[ 1 ]-<field>
exp = '<expected_value>'
msg = '<assertion message>' ).
ENDMETHOD.
" ... more test methods
ENDCLASS.
Test Data Generation Rules
Follow these principles when generating test data:
- Minimal rows: Use the fewest rows needed to exercise the semantic. Typically 1-3 rows per test.
- Obvious values: Choose values where the expected result is trivially verifiable:
- For
gross - discount = net: use 100.00 - 10.00 = 90.00, not 37.83 - 4.27
- For CASE branches: use exactly the values from the CASE expression
- For WHERE filters: one row that passes, one that doesn't
- Type-correct: Match ABAP types exactly:
- Use
'001' for NUMC fields (string with leading zeros)
- Use
'100.00' for DEC/CURR fields
- Use
'20260101' for DATS fields
- Use
'EUR' for CUKY fields
- Key fields populated: Always fill key fields even if not directly tested
- Deterministic: Never use random or date-dependent values
- Isolated: Each test method should be self-contained (clear_doubles in setup ensures this)
Naming Conventions
- Test class:
ZCL_TEST_<ENTITY> or ZCL_<ENTITY>_TEST
- Test methods:
test_<semantic>_<detail> (e.g., test_status_case_open, test_net_amount_calc)
- Use snake_case for method names (ABAP convention for test methods)
- Keep method names under 30 characters
Step 5: Preview and Confirm
Show the user the complete generated test class source code and ask:
"Here's the generated test class. Should I create it on the SAP system? (yes / edit first / cancel)"
If the user wants edits, incorporate them before proceeding.
Step 6: Create, Activate, and Test
6-pre. Lint-check generated code (optional)
Before writing, validate the generated code against lint rules:
SAPLint(action="lint", source="<generated_source>", name="<test_class_name>")
Fix any lint findings before proceeding. Pre-write lint validation also runs automatically when enabled (default: on).
6a. Create the test class
SAPWrite(action="create", type="CLAS", name="<test_class_name>", source="<generated_source>", package="<package>", transport="<transport>")
6b. Update the source (if create only scaffolds)
SAPWrite(action="update", type="CLAS", name="<test_class_name>", source="<generated_source>", transport="<transport>")
6c. Activate the test class
SAPActivate(type="CLAS", name="<test_class_name>")
Activation returns structured responses with detailed error/warning messages including line numbers. Use these to pinpoint exact issues.
6d. Run the unit tests
SAPDiagnose(action="unittest", type="CLAS", name="<test_class_name>")
6e. Report results
Show the user:
- Number of tests passed / failed / skipped
- For any failures: the assertion message and which test method failed
- Suggested fixes for any failures
If tests fail:
- Analyze the failure message
- Determine if it's a test data issue, assertion issue, or CDS logic issue
- Fix the test method using
SAPWrite(action="edit_method", ...)
- Re-activate and re-run
Error Handling
Common Issues and Fixes
| Error | Cause | Fix |
|---|
CX_CDS_TEST_ENVIRONMENT during class_setup | Entity name wrong or not active | Verify entity name, check activation status |
| Type mismatch in VALUE constructor | Wrong field type in test data | Check DD03L or table structure for correct types |
GET_DOUBLE fails for a data source | Data source name doesn't match | Check CDS DDL for exact data source names (case-sensitive!) |
| Assertion fails with unexpected values | Test data or expected result wrong | Re-check CDS logic, adjust test data or assertion |
| Activation error: "test relation not found" | @testing annotation points to wrong entity | Fix entity name in "! @testing comment |
| Class exists already | Test class already created | Use SAPWrite(action="update", ...) instead |
Notes
BTP vs On-Premise Differences
- BTP: Only Z*/Y* classes can be created. Standard SAP tables cannot be queried via DD03L. Use released CDS entities for type reference.
- On-Premise: Full access to DD03L, DD02L for field catalogs. Can query TADIR for object existence checks.
What This Skill Does NOT Do
- Performance testing: CDS Test Doubles are for logic testing, not performance
- Authorization testing: DCL/CDS access control testing requires separate patterns
- Integration testing: This generates isolated unit tests, not end-to-end flows
- RAP behavior testing: For validations/actions/determinations, use the RAP BO Test Double Framework instead
When to Use This Skill
- When a CDS entity has non-trivial logic (calculations, CASE, filters, joins)
- When the user wants basic test coverage as a starting point
- When refactoring CDS logic and wanting regression tests
- NOT for trivial projections that just rename fields — if analysis in Step 2 finds no testable semantics (just field renames with no calculations, CASE, WHERE, JOINs, or aggregations), warn the user: "This CDS view is a simple projection with no testable logic. Unit tests would only verify that fields are correctly aliased, which provides minimal value. Consider testing the underlying view instead."