L’ABAP Test Cockpit (ATC) est l’outil central de SAP pour l’analyse statique du code. Il vérifie automatiquement le code ABAP pour détecter les erreurs, les problèmes de performance, les failles de sécurité et les violations des directives de codage. Dans ABAP Cloud, ATC est indispensable pour garantir la qualité de votre code.
Qu’est-ce que l’ABAP Test Cockpit ?
ATC est un framework pour les vérifications statiques du code qui combine différentes catégories de contrôles :
| Composant | Description |
|---|---|
| ATC Checks | Analyses statiques du code pour la syntaxe, la performance, la sécurité |
| Code Inspector | Framework de base pour les vérifications (Transaction SCI) |
| ABAP Unit | Intégration des résultats des tests unitaires |
| Custom Checks | Définir ses propres règles de vérification |
Avantages d’ATC
- Détection précoce des erreurs : Trouver les problèmes avant le transport
- Qualité constante : Standards uniformes pour toute l’équipe
- Sécurité : Détecter automatiquement les failles de sécurité
- Conformité ABAP Cloud : Utiliser uniquement les APIs autorisées
- Intégration CI/CD : Vérifications automatisées dans le pipeline
Exécuter ATC dans ADT
Dans les ABAP Development Tools (ADT), ATC est directement intégré.
Vérification rapide d’objets individuels
1. Ouvrir l'objet dans ADT (Classe, Report, CDS View)2. Clic droit → Run As → ABAP Test Cockpit ou raccourci clavier : Ctrl+Shift+F23. Résultats dans la vue "ATC Problems"ATC pour des packages complets
1. Sélectionner le package dans le Project Explorer2. Clic droit → Run As → ABAP Test Cockpit With...3. Sélectionner la Check Variant (ex. DEFAULT, ABAP_CLOUD)4. Run5. Analyser les résultatsExemple pratique : Vérifier une classe
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. " Démonstration : Cette classe a intentionnellement des findings ATC DATA: lt_items TYPE TABLE OF i.
" Finding ATC : Variable non utilisée 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. " Finding ATC : Éviter le typage générique LOOP AT it_items ASSIGNING FIELD-SYMBOL(<item>). rv_total = rv_total + <item>. ENDLOOP. ENDMETHOD.
ENDCLASS.Findings ATC attendus :
FINDING 1 (Priorité 2): La variable 'LV_UNUSED' est déclarée mais non utilisée → Solution : Supprimer la variable
FINDING 2 (Priorité 3): Éviter le typage générique de IT_ITEMS → Solution : Utiliser un type de table concretCatégories de vérification importantes
ATC regroupe les vérifications en différentes catégories :
Syntaxe et robustesse
" MAUVAIS : Division sans vérificationDATA(lv_result) = lv_total / lv_count. " ATC : Division par zéro possible
" BON : Avec vérificationIF lv_count <> 0. DATA(lv_result) = lv_total / lv_count.ENDIF.Performance
" MAUVAIS : SELECT dans une boucleLOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>). SELECT SINGLE * FROM vbak " ATC : SELECT dans une boucle (N+1) WHERE vbeln = @<order>-vbeln INTO @DATA(ls_header).ENDLOOP.
" BON : SELECT en masseSELECT * FROM vbak FOR ALL ENTRIES IN @lt_orders WHERE vbeln = @lt_orders-vbeln INTO TABLE @DATA(lt_headers).Sécurité
" MAUVAIS : Injection SQL possibleDATA(lv_where) = |CARRID = '{ lv_input }'|.SELECT * FROM sflight WHERE (lv_where) " ATC : WHERE dynamique sans échappement INTO TABLE @DATA(lt_flights).
" BON : Avec échappement ou paramètresSELECT * FROM sflight WHERE carrid = @lv_input " Basé sur les paramètres INTO TABLE @DATA(lt_flights).Conformité ABAP Cloud
" MAUVAIS : API non autoriséeCALL FUNCTION 'POPUP_TO_CONFIRM'. " ATC : API non autorisée pour ABAP Cloud
" BON : Utiliser une API autorisée" Dans RAP : Utiliser le Message Handler" Dans Console : IF_OO_ADT_CLASSRUN pour les sortiesConventions de nommage
" ATC vérifie les conventions de nommage :" - Préfixe LV_ pour les variables locales" - Préfixe LS_ pour les structures locales" - Préfixe LT_ pour les tables locales" - Préfixe IV_ pour les paramètres Importing" - Préfixe RV_ pour les paramètres Returning
" MAUVAISDATA: total TYPE i.DATA: items TYPE TABLE OF i.
" BONDATA: lv_total TYPE i.DATA: lt_items TYPE TABLE OF i.Priorités et exemptions
Les findings ATC ont des priorités qui indiquent l’urgence :
| Priorité | Signification | Action |
|---|---|---|
| 1 | Critique / Erreur | Doit être corrigé |
| 2 | Important / Avertissement | Devrait être corrigé |
| 3 | Recommandation | Peut être corrigé |
Demander une exemption
Parfois les findings sont des faux positifs ou consciemment acceptés :
1. Dans la vue ATC Problems : Sélectionner le finding2. Clic droit → Request Exemption3. Saisir la justification : "L'intégration legacy nécessite du SQL dynamique"4. Sélectionner l'approbateur5. Submit RequestWorkflow d’exemption
Processus d'exemption :
1. Le développeur demande une exemption ↓2. L'approbateur vérifie la justification ↓3. Approbation ou rejet ↓4. En cas d'approbation : Le finding est masquéDurée de validité de l’exemption :
- Permanente : Pour les décisions d'architecture délibérées- Limitée dans le temps : Pour les workarounds temporaires (6-12 mois)- Spécifique à l'objet : S'applique uniquement à l'objet concerné- Multi-packages : S'applique à tous les objets du packageIntégration CI/CD
ATC peut être intégré dans les pipelines CI/CD pour vérifier automatiquement la qualité du code.
Appeler 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. " Exécuter ATC pour un package DATA(lt_findings) = run_atc_check( 'ZRAP_DEMO' ).
out->write( |Findings ATC : { lines( lt_findings ) }| ).
LOOP AT lt_findings INTO DATA(ls_finding). out->write( |{ ls_finding-priority }: { ls_finding-object_name } - { ls_finding-message }| ). ENDLOOP.
" Vérifier les findings critiques 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( |ERREUR : { lv_critical } findings critiques !| ). ELSE. out->write( 'OK : Aucun finding critique.' ). ENDIF. ENDMETHOD.
METHOD run_atc_check. " Exemple simplifié - présentation conceptuelle " L'API ATC réelle est plus complexe
" En pratique : " 1. CL_CI_OBJECTSET pour la sélection d'objets " 2. CL_CI_INSPECTION pour la vérification " 3. CL_CI_INSPECTION->GET_RESULTS pour les résultats
" Findings d'exemple pour la démo rt_findings = VALUE #( ( object_type = 'CLAS" object_name = 'ZCL_EXAMPLE" priority = 2 message = 'Variable non utilisée' ) ( object_type = 'CLAS" object_name = 'ZCL_EXAMPLE" priority = 3 message = 'Longueur de méthode dépassée' ) ). ENDMETHOD.
ENDCLASS.Intégration SAP CI/CD Service
Dans le SAP CI/CD Service, ATC est intégré via la configuration du pipeline :
# .pipeline/config.yml pour 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 avec abaplint
Pour les projets open source avec 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 "Les vérifications équivalentes ATC ont échoué !" exit 1 fiCréer des Custom Checks
ATC peut être étendu avec ses propres vérifications.
Classe Custom Check
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 = 'Vérifie la longueur maximale des méthodes'. category = 'ZCL_CUSTOM_CHECKS'. has_attributes = abap_true. ENDMETHOD.
METHOD run. " Implémentation de la logique de vérification " Analyse du code source pour la longueur des méthodes
" Exemple simplifié : " 1. Identifier les méthodes dans l'objet vérifié " 2. Compter le nombre de statements par méthode " 3. En cas de dépassement : créer un finding
DATA(lv_method_statements) = 75. " Valeur d'exemple
IF lv_method_statements > mv_max_statements. " Créer un 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.Enregistrer le Custom Check
1. Ouvrir la transaction SCI2. Code Inspector → Éditer la Check Variant3. Sélectionner votre propre classe Check ZCL_ATC_CHECK_*4. Inclure dans la Check Variant5. ActiverDéfinir les messages
Transaction SE91 - Créer une classe de messages :
Classe de messages : ZCL_ATC_MESSAGESMessages : 001 : La méthode &1 a &2 statements (max : &3) 002 : La classe &1 a trop de méthodes publiques 003 : Le type de table &1 devrait être SORTEDExemples pratiques
Exemple 1 : Développer une classe conforme ATC
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. " Initialisation rs_result-valid = abap_true.
" Valider le client IF NOT validate_customer( is_order-customer ). rs_result-valid = abap_false. APPEND 'Client non trouvé ou bloqué' TO rs_result-messages. ENDIF.
" Valider le montant IF NOT validate_amount( iv_amount = is_order-amount iv_currency = is_order-currency ). rs_result-valid = abap_false. APPEND 'Montant ou devise invalide' TO rs_result-messages. ENDIF.
" Valider le statut 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 |Statut invalide : { is_order-status }| TO rs_result-messages. ENDIF. ENDMETHOD.
METHOD validate_customer. " Conforme ATC : Pas de division, pas de statements dynamiques 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. " Le montant doit être positif IF iv_amount <= 0. rv_valid = abap_false. RETURN. ENDIF.
" La devise doit exister SELECT SINGLE @abap_true FROM tcurc WHERE waers = @iv_currency INTO @rv_valid.
IF sy-subrc <> 0. rv_valid = abap_false. ENDIF. ENDMETHOD.
ENDCLASS.Résultat ATC : Aucun finding
Exemple 2 : Corriger les findings ATC
" === AVANT : Avec findings ATC ===
CLASS zcl_report_generator DEFINITION PUBLIC FINAL.
PUBLIC SECTION. " Finding : Déclaration CREATE manquante METHODS generate.
ENDCLASS.
CLASS zcl_report_generator IMPLEMENTATION. METHOD generate. " Finding 1 : Variable non utilisée DATA: lv_unused TYPE string.
" Finding 2 : Éviter SELECT * SELECT * FROM sflight INTO TABLE @DATA(lt_flights).
" Finding 3 : Littéral codé en dur IF lines( lt_flights ) > 100. " Finding 4 : WRITE non autorisé dans Cloud WRITE 'Trop d''enregistrements'. ENDIF. ENDMETHOD.ENDCLASS.
" === APRÈS : Conforme ATC ===
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( |Vols chargés : { lines( lt_flights ) }| ). ENDMETHOD.
METHOD generate. " Sélectionner uniquement les champs nécessaires SELECT carrid, connid, fldate, price, currency FROM sflight INTO CORRESPONDING FIELDS OF TABLE @rt_flights UP TO @c_max_records ROWS.
" Pas de WRITE - sortie via Interface ou valeur de retour ENDMETHOD.
ENDCLASS.Exemple 3 : Optimisation de performance après 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. " Exemple d'appel DATA(lt_ranges) = VALUE tt_range_i( ( sign = 'I' option = 'BT' low = 1 high = 1000 ) ).
DATA(lt_details) = process_orders( lt_ranges ). out->write( |Commandes traitées : { lines( lt_details ) }| ). ENDMETHOD.
METHOD process_orders. " Conforme ATC : Pas de SELECTs dans les boucles
" Étape 1 : Toutes les commandes en un seul 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.
" Étape 2 : Données clients en un seul 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).
" Étape 3 : Fusionner en mémoire 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 ).
" Assigner le nom du client 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 utilise des Check Variants pour définir quelles vérifications sont exécutées.
Check Variants standard
| Variante | Description | Application |
|---|---|---|
DEFAULT | Vérifications de base | Développement général |
ABAP_CLOUD | Conformité ABAP Cloud | Développement Tier-1 |
ABAP_CLOUD_DEVELOPMENT | Vérification Cloud stricte | BTP/S/4HANA Public Cloud |
PERFORMANCE | Vérifications de performance | Optimisation |
SECURITY | Vérifications de sécurité | Security Reviews |
Créer sa propre Check Variant
1. Ouvrir la transaction SCI2. Check Variant → Create3. Nom : ZPROJECT_CHECKS4. Sélectionner les vérifications : ✓ Syntax Check ✓ ABAP Cloud Readiness ✓ Performance (sélectionnées) ✓ Security (toutes) ✓ Custom Checks5. Définir les priorités6. ActiverAppliquer la Check Variant dans l’équipe
Paramètres du package :
1. Ouvrir le package dans SE21/ADT2. Paramètres ATC3. Check Variant : ZPROJECT_CHECKS4. Niveau d'application : - Warning : ATC affiche les findings - Error : Blocage du transport pour les findings P15. SauvegarderBonnes pratiques
1. Exécuter ATC tôt et souvent
Workflow de développement :✓ Après chaque modification majeure : Exécuter ATC✓ Avant chaque commit : ATC sans findings✓ Avant le transport : Exécution ATC complète✓ Dans CI/CD : Vérification automatique2. Traiter correctement les priorités
Priorité 1 (Critique) :→ TOUJOURS corriger avant le transport→ Pas d'exemptions sauf exceptions
Priorité 2 (Important) :→ Devrait être corrigé→ Exemption possible avec bonne justification
Priorité 3 (Recommandation) :→ Corriger quand c'est possible→ Exemption plus facile à obtenir3. Définir des standards d’équipe
Gouvernance ATC :- Définir la Check Variant pour le projet- Documenter le processus d'exemption- Nombre maximum de findings ouverts par package- Revues régulières de la dette technique4. Traiter systématiquement les findings
1. Trier par priorité (1 → 2 → 3)2. Regrouper par objet3. Corriger les findings similaires ensemble4. Après correction : Exécuter à nouveau ATC5. Traiter immédiatement les nouveaux findingsConclusion
L’ABAP Test Cockpit est indispensable pour le développement ABAP professionnel :
- Vérification automatisée de la qualité : Détecter les erreurs tôt
- Conformité ABAP Cloud : Utiliser uniquement les APIs autorisées
- Performance : Éviter les problèmes d’exécution
- Sécurité : Trouver les failles de sécurité
- CI/CD : Intégration transparente dans le pipeline
Faites d’ATC une partie intégrante de votre workflow de développement pour fournir une qualité de code constamment élevée.
Articles connexes
- abapGit : Versioning Git pour ABAP
- ABAP Unit Testing
- Clean ABAP Best Practices
- Guide de migration ABAP Cloud