ABAP Test Cockpit (ATC) : Vérifier automatiquement la qualité du code

Catégorie
DevOps
Publié
Auteur
Johannes

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 :

ComposantDescription
ATC ChecksAnalyses statiques du code pour la syntaxe, la performance, la sécurité
Code InspectorFramework de base pour les vérifications (Transaction SCI)
ABAP UnitIntégration des résultats des tests unitaires
Custom ChecksDé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+F2
3. Résultats dans la vue "ATC Problems"

ATC pour des packages complets

1. Sélectionner le package dans le Project Explorer
2. Clic droit → Run As → ABAP Test Cockpit With...
3. Sélectionner la Check Variant (ex. DEFAULT, ABAP_CLOUD)
4. Run
5. Analyser les résultats

Exemple 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 concret

Catégories de vérification importantes

ATC regroupe les vérifications en différentes catégories :

Syntaxe et robustesse

" MAUVAIS : Division sans vérification
DATA(lv_result) = lv_total / lv_count. " ATC : Division par zéro possible
" BON : Avec vérification
IF lv_count <> 0.
DATA(lv_result) = lv_total / lv_count.
ENDIF.

Performance

" MAUVAIS : SELECT dans une boucle
LOOP 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 masse
SELECT * FROM vbak
FOR ALL ENTRIES IN @lt_orders
WHERE vbeln = @lt_orders-vbeln
INTO TABLE @DATA(lt_headers).

Sécurité

" MAUVAIS : Injection SQL possible
DATA(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ètres
SELECT * FROM sflight
WHERE carrid = @lv_input " Basé sur les paramètres
INTO TABLE @DATA(lt_flights).

Conformité ABAP Cloud

" MAUVAIS : API non autorisée
CALL 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 sorties

Conventions 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
" MAUVAIS
DATA: total TYPE i.
DATA: items TYPE TABLE OF i.
" BON
DATA: 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éSignificationAction
1Critique / ErreurDoit être corrigé
2Important / AvertissementDevrait être corrigé
3RecommandationPeut ê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 finding
2. Clic droit → Request Exemption
3. Saisir la justification :
"L'intégration legacy nécessite du SQL dynamique"
4. Sélectionner l'approbateur
5. Submit Request

Workflow 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 package

Inté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 Service
stages:
- 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 :

.github/workflows/atc.yml
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
fi

Cré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 SCI
2. Code Inspector → Éditer la Check Variant
3. Sélectionner votre propre classe Check ZCL_ATC_CHECK_*
4. Inclure dans la Check Variant
5. Activer

Définir les messages

Transaction SE91 - Créer une classe de messages :
Classe de messages : ZCL_ATC_MESSAGES
Messages :
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 SORTED

Exemples 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

VarianteDescriptionApplication
DEFAULTVérifications de baseDéveloppement général
ABAP_CLOUDConformité ABAP CloudDéveloppement Tier-1
ABAP_CLOUD_DEVELOPMENTVérification Cloud stricteBTP/S/4HANA Public Cloud
PERFORMANCEVérifications de performanceOptimisation
SECURITYVérifications de sécuritéSecurity Reviews

Créer sa propre Check Variant

1. Ouvrir la transaction SCI
2. Check Variant → Create
3. Nom : ZPROJECT_CHECKS
4. Sélectionner les vérifications :
✓ Syntax Check
✓ ABAP Cloud Readiness
✓ Performance (sélectionnées)
✓ Security (toutes)
✓ Custom Checks
5. Définir les priorités
6. Activer

Appliquer la Check Variant dans l’équipe

Paramètres du package :
1. Ouvrir le package dans SE21/ADT
2. Paramètres ATC
3. Check Variant : ZPROJECT_CHECKS
4. Niveau d'application :
- Warning : ATC affiche les findings
- Error : Blocage du transport pour les findings P1
5. Sauvegarder

Bonnes 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 automatique

2. 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 à obtenir

3. 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 technique

4. Traiter systématiquement les findings

1. Trier par priorité (1 → 2 → 3)
2. Regrouper par objet
3. Corriger les findings similaires ensemble
4. Après correction : Exécuter à nouveau ATC
5. Traiter immédiatement les nouveaux findings

Conclusion

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