generate-cds-unit-test — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited generate-cds-unit-test (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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).
| 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 |
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:
ZCL_TEST_<entity_name>)$TMP)$TMP packages)Read the CDS entity, its field structure, and all dependencies in just 3 tool calls.
SAPRead(type="DDLS", name="<entity_name>")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.
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:
For deeper dependency graphs (e.g., a consumption view → interface view → table), use depth=2:
SAPContext(type="DDLS", name="<entity_name>", depth=2)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.
Preferred (SAP_BASIS 8.16+ — ABAP Platform 2025 / S/4HANA 2025): ask SAP directly. SAP analyzes the entity for you and returns the authoritative list of testable semantics — the same CDS Test Double Framework engine behind SAP Joule's CDS unit-test generation:
SAPDiagnose(action="cds_testcases", name="<entity_name>")Each suggestion carries a ready testMethod name, a semanticType (NONE = whole view, CALCULATION, CAST, JOIN, CASE, …), a description, and — for calculated/cast cases — the calculatedField. Use this as your proposed test-case list for the "Output to User" block below: it is SAP's own analysis, so prefer it over hand-parsing the DDL. Map each semanticType onto the category table and each testMethod onto a FOR TESTING method in Step 4. If the tool returns a "requires SAP_BASIS 8.16+" message (older releases / NW 7.5x don't expose the endpoint), or you want to cross-check coverage, use the manual analysis below.
Fallback / cross-check — analyze the DDL yourself. Analyze the CDS DDL source you read in Step 1 and identify all testable semantics. Group them by category:
| 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 |
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 valueAsk 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.
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:
cl_cds_test_environment=>create() signatureget_double() / insert() patternGenerate a complete ABAP test class following this structure:
"! @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.Follow these principles when generating test data:
gross - discount = net: use 100.00 - 10.00 = 90.00, not 37.83 - 4.27'001' for NUMC fields (string with leading zeros)'100.00' for DEC/CURR fields'20260101' for DATS fields'EUR' for CUKY fieldsZCL_TEST_<ENTITY> or ZCL_<ENTITY>_TESTtest_<semantic>_<detail> (e.g., test_status_case_open, test_net_amount_calc)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.
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).
SAPWrite(action="create", type="CLAS", name="<test_class_name>", source="<generated_source>", package="<package>", transport="<transport>")SAPWrite(action="update", type="CLAS", name="<test_class_name>", source="<generated_source>", transport="<transport>")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.
SAPDiagnose(action="unittest", type="CLAS", name="<test_class_name>")Show the user:
If tests fail:
SAPWrite(action="edit_method", ...)| 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 |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.