Das ABAP Test Cockpit (ATC) ist SAPs zentrales Werkzeug zur statischen Code-Analyse. Es prüft ABAP-Code automatisiert auf Fehler, Performance-Probleme, Sicherheitslücken und Verstöße gegen Coding-Guidelines. In ABAP Cloud ist ATC unverzichtbar, um die Qualität deines Codes zu sichern.
Was ist das ABAP Test Cockpit?
ATC ist ein Framework für statische Code-Prüfungen, das verschiedene Check-Kategorien kombiniert:
| Komponente | Beschreibung |
|---|---|
| ATC Checks | Statische Code-Analysen für Syntax, Performance, Security |
| Code Inspector | Basis-Framework für Prüfungen (Transaktion SCI) |
| ABAP Unit | Integration von Unit-Test-Ergebnissen |
| Custom Checks | Eigene Prüfregeln definieren |
Vorteile von ATC
- Frühe Fehlererkennung: Probleme vor dem Transport finden
- Konsistente Qualität: Einheitliche Standards für das gesamte Team
- Security: Sicherheitslücken automatisch erkennen
- ABAP Cloud Compliance: Nur freigegebene APIs verwenden
- CI/CD Integration: Automatisierte Prüfungen in der Pipeline
ATC in ADT ausführen
In den ABAP Development Tools (ADT) ist ATC direkt integriert.
Quick Check einzelner Objekte
1. Objekt in ADT öffnen (Klasse, Report, CDS View)2. Rechtsklick → Run As → ABAP Test Cockpit oder Tastenkürzel: Ctrl+Shift+F23. Ergebnisse im "ATC Problems" ViewATC für komplette Packages
1. Package im Project Explorer auswählen2. Rechtsklick → Run As → ABAP Test Cockpit With...3. Check Variant auswählen (z.B. DEFAULT, ABAP_CLOUD)4. Run5. Ergebnisse analysierenPraktisches Beispiel: Klasse prüfen
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: Diese Klasse hat absichtlich ATC-Findings DATA: lt_items TYPE TABLE OF i.
" ATC Finding: Variable nicht verwendet 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: Generic typing vermeiden LOOP AT it_items ASSIGNING FIELD-SYMBOL(<item>). rv_total = rv_total + <item>. ENDLOOP. ENDMETHOD.
ENDCLASS.Erwartete ATC-Findings:
FINDING 1 (Priority 2): Variable 'LV_UNUSED' wird deklariert aber nicht verwendet → Lösung: Variable entfernen
FINDING 2 (Priority 3): Generische Typisierung von IT_ITEMS vermeiden → Lösung: Konkreten Tabellentyp verwendenWichtige Check-Kategorien
ATC gruppiert Prüfungen in verschiedene Kategorien:
Syntax und Robustheit
" SCHLECHT: Division ohne PrüfungDATA(lv_result) = lv_total / lv_count. " ATC: Mögliche Division durch Null
" GUT: Mit PrüfungIF lv_count <> 0. DATA(lv_result) = lv_total / lv_count.ENDIF.Performance
" SCHLECHT: SELECT in SchleifeLOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>). SELECT SINGLE * FROM vbak " ATC: SELECT in Schleife (N+1) WHERE vbeln = @<order>-vbeln INTO @DATA(ls_header).ENDLOOP.
" GUT: Bulk SELECTSELECT * FROM vbak FOR ALL ENTRIES IN @lt_orders WHERE vbeln = @lt_orders-vbeln INTO TABLE @DATA(lt_headers).Security
" SCHLECHT: SQL Injection möglichDATA(lv_where) = |CARRID = '{ lv_input }'|.SELECT * FROM sflight WHERE (lv_where) " ATC: Dynamisches WHERE ohne Escaping INTO TABLE @DATA(lt_flights).
" GUT: Mit Escaping oder ParameternSELECT * FROM sflight WHERE carrid = @lv_input " Parameter-basiert INTO TABLE @DATA(lt_flights).ABAP Cloud Compliance
" SCHLECHT: Nicht freigegebene APICALL FUNCTION 'POPUP_TO_CONFIRM'. " ATC: API nicht für ABAP Cloud freigegeben
" GUT: Freigegebene API verwenden" In RAP: Message Handler nutzen" In Console: IF_OO_ADT_CLASSRUN für AusgabenNamenskonventionen
" ATC prüft Namenskonventionen:" - LV_ Prefix für lokale Variablen" - LS_ Prefix für lokale Strukturen" - LT_ Prefix für lokale Tabellen" - IV_ Prefix für Importing-Parameter" - RV_ Prefix für Returning-Parameter
" SCHLECHTDATA: total TYPE i.DATA: items TYPE TABLE OF i.
" GUTDATA: lv_total TYPE i.DATA: lt_items TYPE TABLE OF i.Prioritäten und Exemptions
ATC-Findings haben Prioritäten, die die Dringlichkeit anzeigen:
| Priorität | Bedeutung | Handlung |
|---|---|---|
| 1 | Kritisch / Error | Muss behoben werden |
| 2 | Wichtig / Warning | Sollte behoben werden |
| 3 | Empfehlung | Kann behoben werden |
Exemption beantragen
Manchmal sind Findings False Positives oder bewusst akzeptiert:
1. Im ATC Problems View: Finding auswählen2. Rechtsklick → Request Exemption3. Begründung eingeben: "Legacy-Integration erfordert dynamisches SQL"4. Genehmiger auswählen5. Submit RequestExemption-Workflow
Exemption-Prozess:
1. Entwickler beantragt Exemption ↓2. Genehmiger prüft Begründung ↓3. Genehmigung oder Ablehnung ↓4. Bei Genehmigung: Finding wird ausgeblendetExemption-Gültigkeitsdauer:
- Permanent: Für bewusste Architekturentscheidungen- Zeitlich begrenzt: Für temporäre Workarounds (6-12 Monate)- Objektspezifisch: Gilt nur für das betroffene Objekt- Paketübergreifend: Gilt für alle Objekte im PackageCI/CD Integration
ATC lässt sich in CI/CD-Pipelines integrieren, um Code-Qualität automatisiert zu prüfen.
ATC über API aufrufen
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. " ATC für ein Package ausführen 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.
" Auf kritische Findings prüfen 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( |FEHLER: { lv_critical } kritische Findings!| ). ELSE. out->write( 'OK: Keine kritischen Findings.' ). ENDIF. ENDMETHOD.
METHOD run_atc_check. " Vereinfachtes Beispiel - konzeptuelle Darstellung " Die tatsächliche ATC-API ist komplexer
" In der Praxis: " 1. CL_CI_OBJECTSET für Objektauswahl " 2. CL_CI_INSPECTION für Prüfung " 3. CL_CI_INSPECTION->GET_RESULTS für Ergebnisse
" Beispiel-Findings für Demo rt_findings = VALUE #( ( object_type = 'CLAS' object_name = 'ZCL_EXAMPLE' priority = 2 message = 'Variable nicht verwendet' ) ( object_type = 'CLAS' object_name = 'ZCL_EXAMPLE' priority = 3 message = 'Methodenlänge überschritten' ) ). ENDMETHOD.
ENDCLASS.SAP CI/CD Service Integration
Im SAP CI/CD Service wird ATC über die Pipeline-Konfiguration eingebunden:
# .pipeline/config.yml für 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 mit abaplint
Für Open-Source-Projekte mit 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-äquivalente Prüfungen fehlgeschlagen!" exit 1 fiCustom Checks erstellen
ATC kann durch eigene Prüfungen erweitert werden.
Custom Check Klasse
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 = 'Prüft maximale Methodenlänge'. category = 'ZCL_CUSTOM_CHECKS'. has_attributes = abap_true. ENDMETHOD.
METHOD run. " Implementierung der Prüflogik " Analyse des Quellcodes auf Methodenlänge
" Vereinfachtes Beispiel: " 1. Methoden im Prüfobjekt identifizieren " 2. Anzahl Statements pro Methode zählen " 3. Bei Überschreitung: Finding erzeugen
DATA(lv_method_statements) = 75. " Beispielwert
IF lv_method_statements > mv_max_statements. " Finding erstellen 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.Custom Check registrieren
1. Transaktion SCI öffnen2. Code Inspector → Check Variant bearbeiten3. Eigene Check-Klasse ZCL_ATC_CHECK_* auswählen4. In Check Variant aufnehmen5. AktivierenNachrichten definieren
Transaktion SE91 - Nachrichtenklasse anlegen:
Nachrichtenklasse: ZCL_ATC_MESSAGESNachrichten: 001: Methode &1 hat &2 Statements (max: &3) 002: Klasse &1 hat zu viele öffentliche Methoden 003: Tabellentyp &1 sollte SORTED seinPraktische Beispiele
Beispiel 1: ATC-konforme Klasse entwickeln
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. " Initialisierung rs_result-valid = abap_true.
" Customer validieren IF NOT validate_customer( is_order-customer ). rs_result-valid = abap_false. APPEND 'Kunde nicht gefunden oder gesperrt' TO rs_result-messages. ENDIF.
" Betrag validieren IF NOT validate_amount( iv_amount = is_order-amount iv_currency = is_order-currency ). rs_result-valid = abap_false. APPEND 'Ungültiger Betrag oder Währung' TO rs_result-messages. ENDIF.
" Status validieren 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 |Ungültiger Status: { is_order-status }| TO rs_result-messages. ENDIF. ENDMETHOD.
METHOD validate_customer. " ATC-konform: Keine Division, keine dynamischen 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. " Betrag muss positiv sein IF iv_amount <= 0. rv_valid = abap_false. RETURN. ENDIF.
" Währung muss existieren 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-Ergebnis: Keine Findings ✓
Beispiel 2: ATC-Findings beheben
" === VORHER: Mit ATC-Findings ===
CLASS zcl_report_generator DEFINITION PUBLIC FINAL.
PUBLIC SECTION. " Finding: Fehlende CREATE-Angabe METHODS generate.
ENDCLASS.
CLASS zcl_report_generator IMPLEMENTATION. METHOD generate. " Finding 1: Variable nicht verwendet DATA: lv_unused TYPE string.
" Finding 2: SELECT * vermeiden SELECT * FROM sflight INTO TABLE @DATA(lt_flights).
" Finding 3: Hardcoded Literal IF lines( lt_flights ) > 100. " Finding 4: WRITE in Cloud nicht erlaubt WRITE 'Zu viele Datensätze'. ENDIF. ENDMETHOD.ENDCLASS.
" === NACHHER: ATC-konform ===
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( |Flüge geladen: { lines( lt_flights ) }| ). ENDMETHOD.
METHOD generate. " Nur benötigte Felder selektieren SELECT carrid, connid, fldate, price, currency FROM sflight INTO CORRESPONDING FIELDS OF TABLE @rt_flights UP TO @c_max_records ROWS.
" Kein WRITE - Ausgabe über Interface oder Rückgabewert ENDMETHOD.
ENDCLASS.Beispiel 3: Performance-Optimierung nach 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. " Beispielaufruf DATA(lt_ranges) = VALUE tt_range_i( ( sign = 'I' option = 'BT' low = 1 high = 1000 ) ).
DATA(lt_details) = process_orders( lt_ranges ). out->write( |Verarbeitete Aufträge: { lines( lt_details ) }| ). ENDMETHOD.
METHOD process_orders. " ATC-konform: Keine SELECTs in Schleifen
" Schritt 1: Alle Orders in einem 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.
" Schritt 2: Kundendaten in einem 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).
" Schritt 3: Zusammenführen im Speicher 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 ).
" Kundennamen zuordnen 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 Varianten
ATC verwendet Check Varianten, um zu definieren, welche Prüfungen ausgeführt werden.
Standard Check Varianten
| Variante | Beschreibung | Anwendung |
|---|---|---|
DEFAULT | Basis-Prüfungen | Entwicklung allgemein |
ABAP_CLOUD | ABAP Cloud Compliance | Tier-1 Entwicklung |
ABAP_CLOUD_DEVELOPMENT | Strenge Cloud-Prüfung | BTP/S/4HANA Public Cloud |
PERFORMANCE | Performance-Checks | Optimierung |
SECURITY | Sicherheitsprüfungen | Security Reviews |
Eigene Check Variante erstellen
1. Transaktion SCI öffnen2. Check Variant → Create3. Name: ZPROJECT_CHECKS4. Prüfungen auswählen: ✓ Syntax Check ✓ ABAP Cloud Readiness ✓ Performance (ausgewählte) ✓ Security (alle) ✓ Custom Checks5. Prioritäten festlegen6. AktivierenCheck Variante im Team durchsetzen
Package-Einstellungen:
1. Package in SE21/ADT öffnen2. ATC-Einstellungen3. Check Variante: ZPROJECT_CHECKS4. Enforcement Level: - Warning: ATC zeigt Findings - Error: Transport-Sperre bei Findings P15. SpeichernBest Practices
1. ATC früh und oft ausführen
Entwicklungsworkflow:✓ Nach jeder größeren Änderung: ATC ausführen✓ Vor jedem Commit: ATC ohne Findings✓ Vor Transport: Vollständiger ATC-Lauf✓ In CI/CD: Automatische Prüfung2. Prioritäten richtig behandeln
Priorität 1 (Critical):→ IMMER beheben vor Transport→ Keine Exemptions außer in Ausnahmen
Priorität 2 (Important):→ Sollte behoben werden→ Exemption mit guter Begründung möglich
Priorität 3 (Recommendation):→ Bei Gelegenheit beheben→ Exemption einfacher zu bekommen3. Team-Standards definieren
ATC-Governance:- Check Variante für das Projekt festlegen- Exemption-Prozess dokumentieren- Maximale offene Findings pro Package- Regelmäßige Technical-Debt-Reviews4. Findings systematisch abarbeiten
1. Nach Priorität sortieren (1 → 2 → 3)2. Nach Objekt gruppieren3. Ähnliche Findings zusammen beheben4. Nach Behebung: ATC erneut ausführen5. Neue Findings sofort bearbeitenFazit
Das ABAP Test Cockpit ist unverzichtbar für professionelle ABAP-Entwicklung:
- Automatisierte Qualitätsprüfung: Fehler früh erkennen
- ABAP Cloud Compliance: Nur freigegebene APIs verwenden
- Performance: Laufzeitprobleme vermeiden
- Security: Sicherheitslücken finden
- CI/CD: Nahtlose Pipeline-Integration
Mache ATC zu einem festen Bestandteil deines Entwicklungsworkflows, um konsistent hohe Code-Qualität zu liefern.
Weiterführende Artikel
- abapGit: Git-Versionierung für ABAP
- ABAP Unit Testing
- Clean ABAP Best Practices
- ABAP Cloud Migration Guide