The ABAP Test Cockpit (ATC) is SAP’s central tool for static code analysis. It automatically checks ABAP code for errors, performance issues, security vulnerabilities, and violations of coding guidelines. In ABAP Cloud, ATC is indispensable for ensuring the quality of your code.
What is the ABAP Test Cockpit?
ATC is a framework for static code checks that combines various check categories:
| Component | Description |
|---|---|
| ATC Checks | Static code analysis for syntax, performance, security |
| Code Inspector | Base framework for checks (Transaction SCI) |
| ABAP Unit | Integration of unit test results |
| Custom Checks | Define your own check rules |
Advantages of ATC
- Early Error Detection: Find problems before transport
- Consistent Quality: Uniform standards for the entire team
- Security: Automatically detect security vulnerabilities
- ABAP Cloud Compliance: Use only released APIs
- CI/CD Integration: Automated checks in the pipeline
Running ATC in ADT
In the ABAP Development Tools (ADT), ATC is directly integrated.
Quick Check of Individual Objects
1. Open object in ADT (class, report, CDS view)2. Right-click -> Run As -> ABAP Test Cockpit or keyboard shortcut: Ctrl+Shift+F23. Results in "ATC Problems" viewATC for Complete Packages
1. Select package in Project Explorer2. Right-click -> Run As -> ABAP Test Cockpit With...3. Select check variant (e.g., DEFAULT, ABAP_CLOUD)4. Run5. Analyze resultsPractical Example: Check a Class
CLASS zcl_atc_demo DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
METHODS calculate_total IMPORTING it_items TYPE STANDARD TABLE RETURNING VALUE(rv_total) TYPE p.
ENDCLASS.
CLASS zcl_atc_demo IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Demonstration: This class intentionally has ATC findings DATA: lt_items TYPE TABLE OF i.
" ATC Finding: Variable not used DATA(lv_unused) = 'Hello'.
lt_items = VALUE #( ( 10 ) ( 20 ) ( 30 ) ).
DATA(lv_total) = calculate_total( lt_items ). out->write( |Total: { lv_total }| ). ENDMETHOD.
METHOD calculate_total. " ATC Finding: Avoid generic typing LOOP AT it_items ASSIGNING FIELD-SYMBOL(<item>). rv_total = rv_total + <item>. ENDLOOP. ENDMETHOD.
ENDCLASS.Expected ATC Findings:
FINDING 1 (Priority 2): Variable 'LV_UNUSED' is declared but not used -> Solution: Remove variable
FINDING 2 (Priority 3): Avoid generic typing of IT_ITEMS -> Solution: Use concrete table typeImportant Check Categories
ATC groups checks into different categories:
Syntax and Robustness
" BAD: Division without checkDATA(lv_result) = lv_total / lv_count. " ATC: Possible division by zero
" GOOD: With checkIF lv_count <> 0. DATA(lv_result) = lv_total / lv_count.ENDIF.Performance
" BAD: SELECT in loopLOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>). SELECT SINGLE * FROM vbak " ATC: SELECT in loop (N+1) WHERE vbeln = @<order>-vbeln INTO @DATA(ls_header).ENDLOOP.
" GOOD: Bulk SELECTSELECT * FROM vbak FOR ALL ENTRIES IN @lt_orders WHERE vbeln = @lt_orders-vbeln INTO TABLE @DATA(lt_headers).Security
" BAD: SQL injection possibleDATA(lv_where) = |CARRID = '{ lv_input }'|.SELECT * FROM sflight WHERE (lv_where) " ATC: Dynamic WHERE without escaping INTO TABLE @DATA(lt_flights).
" GOOD: With escaping or parametersSELECT * FROM sflight WHERE carrid = @lv_input " Parameter-based INTO TABLE @DATA(lt_flights).ABAP Cloud Compliance
" BAD: Non-released APICALL FUNCTION 'POPUP_TO_CONFIRM'. " ATC: API not released for ABAP Cloud
" GOOD: Use released API" In RAP: Use message handler" In Console: IF_OO_ADT_CLASSRUN for outputNaming Conventions
" ATC checks naming conventions:" - LV_ prefix for local variables" - LS_ prefix for local structures" - LT_ prefix for local tables" - IV_ prefix for importing parameters" - RV_ prefix for returning parameters
" BADDATA: total TYPE i.DATA: items TYPE TABLE OF i.
" GOODDATA: lv_total TYPE i.DATA: lt_items TYPE TABLE OF i.Priorities and Exemptions
ATC findings have priorities that indicate urgency:
| Priority | Meaning | Action |
|---|---|---|
| 1 | Critical / Error | Must be fixed |
| 2 | Important / Warning | Should be fixed |
| 3 | Recommendation | Can be fixed |
Request Exemption
Sometimes findings are false positives or intentionally accepted:
1. In ATC Problems View: Select finding2. Right-click -> Request Exemption3. Enter justification: "Legacy integration requires dynamic SQL"4. Select approver5. Submit RequestExemption Workflow
Exemption process:
1. Developer requests exemption |2. Approver reviews justification |3. Approval or rejection |4. If approved: Finding is hiddenExemption Validity Duration:
- Permanent: For deliberate architectural decisions- Time-limited: For temporary workarounds (6-12 months)- Object-specific: Applies only to affected object- Package-wide: Applies to all objects in packageCI/CD Integration
ATC can be integrated into CI/CD pipelines to automatically check code quality.
Call ATC via API
CLASS zcl_atc_runner DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_finding, object_type TYPE trobjtype, object_name TYPE sobj_name, priority TYPE i, message TYPE string, END OF ty_finding, tt_findings TYPE STANDARD TABLE OF ty_finding WITH EMPTY KEY.
METHODS run_atc_check IMPORTING iv_package TYPE devclass RETURNING VALUE(rt_findings) TYPE tt_findings.
ENDCLASS.
CLASS zcl_atc_runner IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Run ATC for a package DATA(lt_findings) = run_atc_check( 'ZRAP_DEMO' ).
out->write( |ATC Findings: { lines( lt_findings ) }| ).
LOOP AT lt_findings INTO DATA(ls_finding). out->write( |{ ls_finding-priority }: { ls_finding-object_name } - { ls_finding-message }| ). ENDLOOP.
" Check for critical findings DATA(lv_critical) = REDUCE i( INIT count = 0 FOR finding IN lt_findings WHERE ( priority <= 2 ) NEXT count = count + 1 ).
IF lv_critical > 0. out->write( |ERROR: { lv_critical } critical findings!| ). ELSE. out->write( 'OK: No critical findings.' ). ENDIF. ENDMETHOD.
METHOD run_atc_check. " Simplified example - conceptual representation " The actual ATC API is more complex
" In practice: " 1. CL_CI_OBJECTSET for object selection " 2. CL_CI_INSPECTION for checking " 3. CL_CI_INSPECTION->GET_RESULTS for results
" Example findings for demo rt_findings = VALUE #( ( object_type = 'CLAS' object_name = 'ZCL_EXAMPLE' priority = 2 message = 'Variable not used' ) ( object_type = 'CLAS' object_name = 'ZCL_EXAMPLE' priority = 3 message = 'Method length exceeded' ) ). ENDMETHOD.
ENDCLASS.SAP CI/CD Service Integration
In the SAP CI/CD Service, ATC is integrated via pipeline configuration:
# .pipeline/config.yml for SAP CI/CD Servicestages: - name: Build steps: - name: abapBuild type: abapEnvironmentBuild
- name: ATC steps: - name: abapEnvironmentRunATCCheck type: abapEnvironmentRunATCCheck config: atcCheckVariant: 'ABAP_CLOUD_DEVELOPMENT' atcConfiguration: '/DEFAULT' failOnSeverity: 'error'GitHub Actions with abaplint
For open-source projects with abapGit:
name: Code Quality
on: push: branches: [ main ] pull_request: branches: [ main ]
jobs: abaplint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20'
- name: Install abaplint run: npm install -g @abaplint/cli
- name: Run abaplint run: abaplint
- name: Check results run: | if [ $? -ne 0 ]; then echo "ATC-equivalent checks failed!" exit 1 fiCreating Custom Checks
ATC can be extended with your own checks.
Custom Check Class
CLASS zcl_atc_check_method_length DEFINITION PUBLIC FINAL CREATE PUBLIC INHERITING FROM cl_ci_test_root.
PUBLIC SECTION. METHODS constructor.
METHODS run REDEFINITION. METHODS get_attributes REDEFINITION. METHODS put_attributes REDEFINITION.
PRIVATE SECTION. DATA mv_max_statements TYPE i VALUE 50.
CONSTANTS c_msg_id TYPE scimessage VALUE 'ZCL_ATC_001'.
ENDCLASS.
CLASS zcl_atc_check_method_length IMPLEMENTATION.
METHOD constructor. super->constructor( ).
description = 'Checks maximum method length'. category = 'ZCL_CUSTOM_CHECKS'. has_attributes = abap_true. ENDMETHOD.
METHOD run. " Implementation of check logic " Analysis of source code for method length
" Simplified example: " 1. Identify methods in check object " 2. Count statements per method " 3. Create finding if exceeded
DATA(lv_method_statements) = 75. " Example value
IF lv_method_statements > mv_max_statements. " Create finding inform( p_sub_obj_name = 'DO_SOMETHING' p_kind = c_error p_test = me->myname p_code = c_msg_id p_param_1 = |{ lv_method_statements }| p_param_2 = |{ mv_max_statements }| ). ENDIF. ENDMETHOD.
METHOD get_attributes. EXPORT max_statements = mv_max_statements TO DATA BUFFER p_attributes. ENDMETHOD.
METHOD put_attributes. IMPORT max_statements = mv_max_statements FROM DATA BUFFER p_attributes. ENDMETHOD.
ENDCLASS.Register Custom Check
1. Open transaction SCI2. Code Inspector -> Edit check variant3. Select custom check class ZCL_ATC_CHECK_*4. Add to check variant5. ActivateDefine Messages
Transaction SE91 - Create message class:
Message Class: ZCL_ATC_MESSAGESMessages: 001: Method &1 has &2 statements (max: &3) 002: Class &1 has too many public methods 003: Table type &1 should be SORTEDPractical Examples
Example 1: Develop an ATC-Compliant Class
CLASS zcl_order_validator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_order, order_id TYPE i, customer TYPE i, amount TYPE p LENGTH 15 DECIMALS 2, currency TYPE waers, status TYPE char1, END OF ty_order.
TYPES: BEGIN OF ty_validation_result, valid TYPE abap_bool, messages TYPE string_table, END OF ty_validation_result.
METHODS validate_order IMPORTING is_order TYPE ty_order RETURNING VALUE(rs_result) TYPE ty_validation_result.
PRIVATE SECTION. METHODS validate_customer IMPORTING iv_customer TYPE i RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS validate_amount IMPORTING iv_amount TYPE p iv_currency TYPE waers RETURNING VALUE(rv_valid) TYPE abap_bool.
ENDCLASS.
CLASS zcl_order_validator IMPLEMENTATION.
METHOD validate_order. " Initialization rs_result-valid = abap_true.
" Validate customer IF NOT validate_customer( is_order-customer ). rs_result-valid = abap_false. APPEND 'Customer not found or blocked' TO rs_result-messages. ENDIF.
" Validate amount IF NOT validate_amount( iv_amount = is_order-amount iv_currency = is_order-currency ). rs_result-valid = abap_false. APPEND 'Invalid amount or currency' TO rs_result-messages. ENDIF.
" Validate status IF is_order-status NOT IN VALUE #( ( sign = 'I' option = 'EQ' low = 'N' ) ( sign = 'I' option = 'EQ' low = 'A' ) ). rs_result-valid = abap_false. APPEND |Invalid status: { is_order-status }| TO rs_result-messages. ENDIF. ENDMETHOD.
METHOD validate_customer. " ATC-compliant: No division, no dynamic statements SELECT SINGLE @abap_true FROM scustom WHERE id = @iv_customer INTO @rv_valid.
IF sy-subrc <> 0. rv_valid = abap_false. ENDIF. ENDMETHOD.
METHOD validate_amount. " Amount must be positive IF iv_amount <= 0. rv_valid = abap_false. RETURN. ENDIF.
" Currency must exist SELECT SINGLE @abap_true FROM tcurc WHERE waers = @iv_currency INTO @rv_valid.
IF sy-subrc <> 0. rv_valid = abap_false. ENDIF. ENDMETHOD.
ENDCLASS.ATC Result: No Findings
Example 2: Fix ATC Findings
" === BEFORE: With ATC Findings ===
CLASS zcl_report_generator DEFINITION PUBLIC FINAL.
PUBLIC SECTION. " Finding: Missing CREATE specification METHODS generate.
ENDCLASS.
CLASS zcl_report_generator IMPLEMENTATION. METHOD generate. " Finding 1: Variable not used DATA: lv_unused TYPE string.
" Finding 2: Avoid SELECT * SELECT * FROM sflight INTO TABLE @DATA(lt_flights).
" Finding 3: Hardcoded literal IF lines( lt_flights ) > 100. " Finding 4: WRITE not allowed in Cloud WRITE 'Too many records'. ENDIF. ENDMETHOD.ENDCLASS.
" === AFTER: ATC-Compliant ===
CLASS zcl_report_generator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
CONSTANTS c_max_records TYPE i VALUE 100.
METHODS generate RETURNING VALUE(rt_flights) TYPE /dmo/t_flight.
ENDCLASS.
CLASS zcl_report_generator IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. DATA(lt_flights) = generate( ). out->write( |Flights loaded: { lines( lt_flights ) }| ). ENDMETHOD.
METHOD generate. " Select only needed fields SELECT carrid, connid, fldate, price, currency FROM sflight INTO CORRESPONDING FIELDS OF TABLE @rt_flights UP TO @c_max_records ROWS.
" No WRITE - output via interface or return value ENDMETHOD.
ENDCLASS.Example 3: Performance Optimization after ATC
CLASS zcl_order_processor DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_order_detail, order_id TYPE i, customer TYPE i, customer_name TYPE string, total TYPE p LENGTH 15 DECIMALS 2, END OF ty_order_detail, tt_order_details TYPE STANDARD TABLE OF ty_order_detail WITH KEY order_id.
METHODS process_orders IMPORTING it_order_ids TYPE tt_range_i RETURNING VALUE(rt_details) TYPE tt_order_details.
ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Example call DATA(lt_ranges) = VALUE tt_range_i( ( sign = 'I' option = 'BT' low = 1 high = 1000 ) ).
DATA(lt_details) = process_orders( lt_ranges ). out->write( |Processed orders: { lines( lt_details ) }| ). ENDMETHOD.
METHOD process_orders. " ATC-compliant: No SELECTs in loops
" Step 1: All orders in one SELECT SELECT order_id, customer, total FROM zorders WHERE order_id IN @it_order_ids INTO TABLE @DATA(lt_orders).
IF lt_orders IS INITIAL. RETURN. ENDIF.
" Step 2: Customer data in one SELECT DATA(lt_customer_ids) = VALUE tt_range_i( FOR order IN lt_orders ( sign = 'I' option = 'EQ' low = CONV #( order-customer ) ) ).
SELECT id, name FROM scustom WHERE id IN @lt_customer_ids INTO TABLE @DATA(lt_customers).
" Step 3: Merge in memory LOOP AT lt_orders INTO DATA(ls_order). DATA(ls_detail) = VALUE ty_order_detail( order_id = ls_order-order_id customer = ls_order-customer total = ls_order-total ).
" Assign customer name READ TABLE lt_customers WITH KEY id = ls_order-customer INTO DATA(ls_customer).
IF sy-subrc = 0. ls_detail-customer_name = ls_customer-name. ENDIF.
APPEND ls_detail TO rt_details. ENDLOOP. ENDMETHOD.
ENDCLASS.Check Variants
ATC uses check variants to define which checks are executed.
Standard Check Variants
| Variant | Description | Application |
|---|---|---|
DEFAULT | Basic checks | General development |
ABAP_CLOUD | ABAP Cloud Compliance | Tier-1 development |
ABAP_CLOUD_DEVELOPMENT | Strict cloud checking | BTP/S/4HANA Public Cloud |
PERFORMANCE | Performance checks | Optimization |
SECURITY | Security checks | Security reviews |
Create Your Own Check Variant
1. Open transaction SCI2. Check Variant -> Create3. Name: ZPROJECT_CHECKS4. Select checks: - Syntax Check - ABAP Cloud Readiness - Performance (selected) - Security (all) - Custom Checks5. Set priorities6. ActivateEnforce Check Variant in Team
Package Settings:
1. Open package in SE21/ADT2. ATC Settings3. Check Variant: ZPROJECT_CHECKS4. Enforcement Level: - Warning: ATC shows findings - Error: Transport lock on P1 findings5. SaveBest Practices
1. Run ATC Early and Often
Development workflow:- After every major change: Run ATC- Before every commit: ATC without findings- Before transport: Complete ATC run- In CI/CD: Automatic checking2. Handle Priorities Correctly
Priority 1 (Critical):-> ALWAYS fix before transport-> No exemptions except in exceptions
Priority 2 (Important):-> Should be fixed-> Exemption possible with good justification
Priority 3 (Recommendation):-> Fix when convenient-> Exemption easier to obtain3. Define Team Standards
ATC Governance:- Define check variant for the project- Document exemption process- Maximum open findings per package- Regular technical debt reviews4. Work Through Findings Systematically
1. Sort by priority (1 -> 2 -> 3)2. Group by object3. Fix similar findings together4. After fixing: Run ATC again5. Address new findings immediatelyConclusion
The ABAP Test Cockpit is indispensable for professional ABAP development:
- Automated Quality Checking: Detect errors early
- ABAP Cloud Compliance: Use only released APIs
- Performance: Avoid runtime problems
- Security: Find security vulnerabilities
- CI/CD: Seamless pipeline integration
Make ATC a fixed part of your development workflow to consistently deliver high code quality.
Further Reading
- abapGit: Git Versioning for ABAP
- ABAP Unit Testing
- Clean ABAP Best Practices
- ABAP Cloud Migration Guide