From ARC-1 — SAP ABAP for Claude
Generate ABAP Unit tests for classes with dependency analysis, interface-based test doubles, and method-level surgical insertion.
How this skill is triggered — by the user, by Claude, or both
Slash command
/arc-1:generate-abap-unit-testThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate ABAP Unit tests for classes with dependency analysis, test doubles, and method-level surgery.
Generate ABAP Unit tests for classes with dependency analysis, test doubles, and method-level surgery.
This skill replicates SAP Joule's "Unit Test Generation" capability for ABAP classes by combining ARC-1 (SAP system access) with mcp-sap-docs (documentation & best practices). Unlike the CDS unit test skill which targets CDS entities, this skill targets ABAP classes and uses interface-based test doubles instead of CDS Test Double Framework.
| Setting | Default | Rationale |
|---|---|---|
| Test class name | ltc_<short_class_name> | ABAP Unit tests for a global class belong in its local testclasses (CCAU) include |
| Methods to test | All public methods | User can narrow after seeing the list |
| Package | $TMP | Fast prototyping |
| Risk level | HARMLESS | Test doubles don't modify real data |
| Duration | SHORT | Unit tests should be fast |
| Mock strategy | Interface-based test doubles | Cleanest pattern for dependency injection |
The user provides an ABAP class to test (e.g., ZCL_TRAVEL_HANDLER).
Only the class name is required. If the user provides just a class name, apply Smart Defaults and proceed immediately.
Optionally, the user may specify:
ltc_<short_class_name> inside the target class's testclasses include)$TMP)$TMP packages)Read the class, its methods, dependencies, and any existing tests.
Prefer the structured format to get metadata and all includes (including existing tests) in one call:
SAPRead(type="CLAS", name="<class_name>", format="structured")
This returns JSON with metadata (description, category, package) and decomposed source (main, testclasses, definitions, implementations, macros). The testclasses field contains existing test code if any — useful for analyzing what's already covered without a separate fetch.
Alternatively, for just the main source:
SAPRead(type="CLAS", name="<class_name>")
SAPRead(type="CLAS", name="<class_name>", method="*")
Returns all methods with their signatures and visibility (public/protected/private). Use this to understand the class API and identify which methods to test.
SAPContext(type="CLAS", name="<class_name>")
Automatically extracts all dependencies and fetches compressed public API contracts for each. This gives you:
For complex classes with deep dependency chains, use depth=2:
SAPContext(type="CLAS", name="<class_name>", depth=2)
If you used format="structured" in Step 1a, the testclasses field already contains existing test code (or null if none) — skip this step.
Otherwise, fetch test classes separately:
SAPRead(type="CLAS", name="<class_name>", include="testclasses")
This may fail if no test classes exist — that's fine. If tests exist, analyze them to avoid duplicating coverage and to follow existing test patterns.
For each public method, analyze the source code to identify testable scenarios.
Classify each dependency the class uses:
| Category | What to Look For | Test Strategy |
|---|---|---|
| Mockable | Constructor injection via interfaces (if_*, zif_*) | Create test double implementing the interface |
| Stubbable | Database access (SELECT, INSERT, UPDATE, DELETE) | Prepare fixture data in SETUP, or use test double for DB layer |
| Transparent | Internal private/protected helper methods | Don't mock — they execute normally |
| Framework | Static calls to SAP framework classes (e.g., cl_abap_context_info) | Wrap in injectable interface or accept limitation |
For each public method, identify:
Present the identified test cases as a numbered list grouped by method:
Identified test cases for ZCL_TRAVEL_HANDLER:
Method: CREATE_TRAVEL (public)
1. [HAPPY] test_create_travel_success — Create travel with valid data, verify travel ID returned
2. [ERROR] test_create_no_customer — Missing customer, expect CX_TRAVEL_ERROR
3. [BRANCH] test_create_with_discount — Discount > 0 triggers calculation path
Method: VALIDATE_DATES (public)
4. [HAPPY] test_dates_valid — End date after begin date
5. [ERROR] test_dates_reversed — Begin date after end date, expect exception
6. [EDGE] test_dates_same_day — Same begin and end date, should be valid
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.
Use mcp-sap-docs to get the latest ABAP Unit and test double patterns.
search("ABAP Unit cl_abap_unit_assert")
search("test double interface mock ABAP")
Use the returned documentation to inform the generated code patterns. Key things to verify:
cl_abap_unit_assert assertion method names (assert_equals, assert_initial, assert_not_initial, assert_bound, fail)FOR TESTING class and method syntaxGenerate a complete ABAP test class following this structure:
"! @testing <CLASS_UNDER_TEST>
CLASS <test_class_name> DEFINITION
FINAL
FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PUBLIC SECTION.
PROTECTED SECTION.
PRIVATE SECTION.
CLASS-DATA cut TYPE REF TO <class_under_test>.
" Test doubles for mockable dependencies
CLASS-DATA mock_<dep1> TYPE REF TO <interface_1>.
CLASS-DATA mock_<dep2> TYPE REF TO <interface_2>.
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.
" One-time initialization — create test doubles
" If using a test double framework:
" mock_<dep1> = cl_abap_testdouble=>create( '<interface_1>' ).
" Or create a local test double class (see below)
ENDMETHOD.
METHOD class_teardown.
" Clean up
ENDMETHOD.
METHOD setup.
" Per-test CUT instantiation with fresh state
" Inject test doubles via constructor
cut = NEW <class_under_test>(
io_<dep1> = mock_<dep1>
io_<dep2> = mock_<dep2>
).
ENDMETHOD.
METHOD <test_method_1>.
" Arrange — configure mock behavior
" cl_abap_testdouble=>configure_call( mock_<dep1>
" )->returning( VALUE #( <expected_return> ) )->and_expect( )->is_called_once( ).
" mock_<dep1>-><method>( ).
" Act — call the method under test
DATA(lv_result) = cut-><method>( <params> ).
" Assert — verify expected behavior
cl_abap_unit_assert=>assert_equals(
act = lv_result
exp = <expected_value>
msg = '<assertion message>' ).
ENDMETHOD.
METHOD <test_method_error>.
" Arrange — set up conditions that trigger the error
" Act & Assert — expect exception
TRY.
cut-><method>( <invalid_params> ).
cl_abap_unit_assert=>fail( msg = 'Expected exception was not raised' ).
CATCH <exception_class> INTO DATA(lx_error).
" Optionally verify exception details
cl_abap_unit_assert=>assert_not_initial(
act = lx_error->get_text( )
msg = 'Exception message should not be empty' ).
ENDTRY.
ENDMETHOD.
" ... more test methods
ENDCLASS.
When cl_abap_testdouble is not available or the interface is complex, create a local test double class:
" Local test double — defined in the test include
CLASS lcl_mock_<dep> DEFINITION FOR TESTING.
PUBLIC SECTION.
INTERFACES <interface>.
DATA last_<method>_input TYPE <param_type>.
DATA <method>_result TYPE <return_type>.
ENDCLASS.
CLASS lcl_mock_<dep> IMPLEMENTATION.
METHOD <interface>~<method>.
last_<method>_input = <param>.
<return_param> = <method>_result.
ENDMETHOD.
ENDCLASS.
Follow these principles when generating test data:
'001' for NUMC fields (string with leading zeros)'100.00' for DEC/CURR fields'20260101' for DATS fieldsabap_true / abap_false for ABAP_BOOLsy-datum, sy-uzeit, cl_abap_context_info=>get_system_date( ), or random valuesltc_<short_class_name> in the class's testclasses includetest_<method>_<scenario> (e.g., test_create_success, test_validate_no_date)mock_<short_dep_name> (e.g., mock_persistence, mock_validator)Show the user the complete generated testclasses include source code and ask:
"Here's the generated testclasses include. Should I write it to 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_testclasses_include>", name="<class_under_test>")
Fix any lint findings before proceeding. Pre-write lint validation also runs automatically when enabled (default: on).
SAPWrite(action="update", type="CLAS", name="<class_under_test>", include="testclasses", source="<generated_testclasses_include>", transport="<transport>")
This is a local include write, not a separate global class create. Do not pass a lock handle or add lock/unlock code to the ABAP source; ARC-1 performs the ADT lock -> initialize missing CCAU include -> write -> unlock sequence internally. On a fresh class, ARC-1 auto-creates the missing testclasses include before writing.
SAPWrite(action="update", type="CLAS", name="<class_under_test>", include="testclasses", source="<full_updated_testclasses_include>", transport="<transport>")
For a small fix to an existing local test method, use method surgery after the include exists:
SAPWrite(action="edit_method", type="CLAS", name="<class_under_test>", method="<ltc_class>~<failing_method>", source="<fixed_method_body>")
Note: For update actions, transport is recommended but not always required. ARC-1 auto-propagates the lock-provided corrNr when no explicit transport is supplied.
SAPActivate(type="CLAS", name="<class_under_test>")
Activation returns structured responses with detailed error/warning messages including line numbers. Use these to pinpoint exact issues.
SAPDiagnose(action="unittest", type="CLAS", name="<class_under_test>")
Show the user:
If tests fail:
SAPWrite(action="edit_method", type="CLAS", name="<class_under_test>", method="<ltc_class>~<failing_method>", source="<fixed_source>")
| Error | Cause | Fix |
|---|---|---|
class_setup fails | Wrong CUT instantiation — missing constructor parameter or wrong type | Check CUT constructor signature, ensure all required dependencies are injected |
| Mock injection fails | Constructor parameter type mismatch — mock doesn't implement expected interface | Verify interface name, check if CUT expects concrete class instead of interface |
| Assertion fails | Wrong expected value or test data | Re-check CUT method logic, adjust expected value or input data |
| Activation error | Syntax error in generated code — typo, wrong type, missing variable declaration | Read activation error, fix syntax, re-activate |
cl_abap_testdouble not found | Test double framework not available on system (older releases) | Use local test double class pattern instead |
| Method not found on CUT | Method is private or protected — not callable from test | Test only public methods; for protected, use FRIENDS clause |
testclasses/CCAU include missing | Fresh class has no ABAP Unit include yet | Use SAPWrite(action="update", type="CLAS", include="testclasses", source=...); ARC-1 auto-initializes it |
Method not found in edit_method | The local ltc_* method does not exist yet, or the wrong local class name was used | First write the full testclasses include with update include="testclasses", then use edit_method for existing methods |
ltc_* test classes into custom Z*/Y* classes. Must use ABAP Cloud syntax — no classic ABAP statements. Only released APIs are available for mocking. cl_abap_testdouble is available. Constructor injection is the standard pattern.cl_abap_testdouble available from 7.51+. On older systems, use local test double classes.npx claudepluginhub arc-mcp/arc-1 --plugin arc-1Guides ABAP Unit test setup: test classes, assertions, test doubles, mocking, dependency injection, CDS/SQL test environments, and RAP BO testing.
Generates ABAP Unit test classes for CDS entities using the CDS Test Double Framework. Automatically reads entity structure, dependencies, and produces typed test code with smart defaults.
Assists with ABAP code for SAP systems: internal tables, structures, ABAP SQL, OOP, RAP, CDS views, EML statements, ABAP Cloud, strings, dynamic programming, RTTI/RTTC, field symbols, data references, exceptions, unit testing.