Tutoriel RAP Partie 3 : Bonnes pratiques, Tests et Performance

Catégorie
Tutorial
Publié
Auteur
Johannes

Bienvenue dans la Partie 3 - la finale de la série de tutoriels RAP ! Vous avez créé dans la Partie 1 et la Partie 2 une application Fiori fonctionnelle avec une logique métier. Maintenant, nous allons la rendre prête pour la production.

Ce que vous allez apprendre

  • Optimisation des performances (Performance SELECT, EML Batching)
  • Tests unitaires pour les RAP Business Objects
  • Gestion des erreurs et Application Logging
  • Bonnes pratiques de déploiement
  • Erreurs courantes et comment les éviter
  • Checklist de revue de code

Temps estimé : 45-60 minutes


Optimisation des performances

Problème 1 : Éviter les SELECT dans les boucles

MAUVAIS :

METHOD validateIsbn.
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
FIELDS ( Isbn BookId )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_books).
LOOP AT lt_books INTO DATA(ls_book).
" SELECT dans la boucle - très lent !
SELECT SINGLE FROM zi_book
FIELDS Isbn
WHERE Isbn = @ls_book-Isbn
AND BookId <> @ls_book-BookId
INTO @DATA(lv_duplicate).
IF sy-subrc = 0.
" L'ISBN existe déjà
ENDIF.
ENDLOOP.
ENDMETHOD.

MIEUX :

METHOD validateIsbn.
READ ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
FIELDS ( Isbn BookId )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_books).
" Un seul SELECT pour tous les ISBN
SELECT Isbn, BookId
FROM zi_book
FOR ALL ENTRIES IN @lt_books
WHERE Isbn = @lt_books-Isbn
INTO TABLE @DATA(lt_all_books).
LOOP AT lt_books INTO DATA(ls_book).
" Vérification des doublons en mémoire
READ TABLE lt_all_books TRANSPORTING NO FIELDS
WITH KEY Isbn = ls_book-Isbn
BINARY SEARCH.
IF sy-tabix > 1. " Plus d'une entrée trouvée
APPEND VALUE #(
%tky = ls_book-%tky
%element-Isbn = if_abap_behv=>mk-on
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |L'ISBN { ls_book-Isbn } existe déjà|
)
) TO reported-book.
ENDIF.
ENDLOOP.
ENDMETHOD.

Gain de performance : ~90% avec 100+ livres


Problème 2 : Regrouper les opérations EML

MAUVAIS :

METHOD processBooks.
LOOP AT lt_book_ids INTO DATA(lv_id).
" MODIFY dans la boucle - beaucoup d'accès DB
MODIFY ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
UPDATE FIELDS ( Status )
WITH VALUE #( ( BookId = lv_id Status = 'F' ) ).
ENDLOOP.
COMMIT ENTITIES.
ENDMETHOD.

MIEUX :

METHOD processBooks.
" Un seul MODIFY pour tous les livres
MODIFY ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
UPDATE FIELDS ( Status )
WITH VALUE #( FOR lv_id IN lt_book_ids
( BookId = lv_id Status = 'F' ) )
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES.
ENDMETHOD.

Gain de performance : ~80% avec 100+ livres


Problème 3 : Performance des CDS Views

Éviter UNION/UNION ALL si possible :

LENT :

define view ZI_BOOK_STATS
as select from zi_book
{
'Total' as Category,
count(*) as BookCount
}
union all
select from zi_book
{
'Finished' as Category,
count(*) as BookCount
}
where Status = 'F';

PLUS RAPIDE :

define view ZI_BOOK_STATS
as select from zi_book
{
Status,
count(*) as BookCount
}
group by Status;

Mesure de performance :

" Tester la performance
DATA(lv_start) = cl_abap_context_info=>get_system_time( ).
SELECT * FROM zi_book_stats INTO TABLE @DATA(lt_stats).
DATA(lv_end) = cl_abap_context_info=>get_system_time( ).
DATA(lv_duration) = lv_end - lv_start.
cl_demo_output=>display( |Durée : { lv_duration } microsecondes| ).

Tests unitaires pour RAP

Créer une classe de test

Ouvrez ZBP_I_BOOK et ajoutez à la fin :

CLASS ltc_book_test DEFINITION FINAL
FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA:
environment TYPE REF TO if_cds_test_environment.
CLASS-METHODS:
class_setup RAISING cx_static_check,
class_teardown.
METHODS:
setup,
teardown,
test_create_book FOR TESTING,
test_start_reading FOR TESTING,
test_validate_isbn_invalid FOR TESTING,
test_validate_pages_zero FOR TESTING.
ENDCLASS.
CLASS ltc_book_test IMPLEMENTATION.
METHOD class_setup.
" Créer l'environnement de test pour la CDS View
environment = cl_cds_test_environment=>create_for_multiple_cds(
i_for_entities = VALUE #(
( i_for_entity = 'ZI_BOOK' )
)
).
ENDMETHOD.
METHOD class_teardown.
environment->destroy( ).
ENDMETHOD.
METHOD setup.
environment->clear_doubles( ).
ENDMETHOD.
METHOD teardown.
ROLLBACK ENTITIES.
ENDMETHOD.
METHOD test_create_book.
" Given : Données de test
DATA lt_books_create TYPE TABLE FOR CREATE zi_book.
lt_books_create = VALUE #(
( %cid = 'BOOK_1"
Title = 'Livre Test"
Author = 'Auteur Test"
Isbn = '1234567890"
Pages = 100 )
).
" When : Créer le livre
MODIFY ENTITIES OF zi_book
ENTITY Book
CREATE FROM lt_books_create
MAPPED DATA(mapped)
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES.
" Then : Aucune erreur, livre créé
cl_abap_unit_assert=>assert_initial(
act = failed
msg = 'La création devrait réussir"
).
cl_abap_unit_assert=>assert_not_initial(
act = mapped-book
msg = 'Le livre devrait être mappé"
).
" Lire le livre et vérifier
READ ENTITIES OF zi_book
ENTITY Book
ALL FIELDS
WITH VALUE #( ( %cid = 'BOOK_1' ) )
RESULT DATA(lt_result)
FAILED failed.
cl_abap_unit_assert=>assert_equals(
act = lt_result[ 1 ]-Title
exp = 'Livre Test"
msg = 'Le titre devrait correspondre"
).
" Le statut devrait être 'N' (Determination)
cl_abap_unit_assert=>assert_equals(
act = lt_result[ 1 ]-Status
exp = 'N"
msg = 'Le statut devrait être N (Nouveau)"
).
ENDMETHOD.
METHOD test_start_reading.
" Given : Livre avec Status 'N"
MODIFY ENTITIES OF zi_book
ENTITY Book
CREATE FROM VALUE #(
( %cid = 'BOOK_1"
Title = 'Test"
Author = 'Auteur"
Status = 'N' )
)
MAPPED DATA(mapped).
COMMIT ENTITIES.
" When : Exécuter l'action startReading
MODIFY ENTITIES OF zi_book
ENTITY Book
EXECUTE startReading FROM VALUE #(
( %cid = 'BOOK_1' )
)
RESULT DATA(result)
FAILED DATA(failed).
COMMIT ENTITIES.
" Then : Le statut devrait être 'R"
READ ENTITIES OF zi_book
ENTITY Book
FIELDS ( Status )
WITH VALUE #( ( %cid = 'BOOK_1' ) )
RESULT DATA(lt_books).
cl_abap_unit_assert=>assert_equals(
act = lt_books[ 1 ]-Status
exp = 'R"
msg = 'Le statut devrait être R (En lecture)"
).
ENDMETHOD.
METHOD test_validate_isbn_invalid.
" Given : Livre avec ISBN invalide (seulement 5 chiffres)
DATA lt_create TYPE TABLE FOR CREATE zi_book.
lt_create = VALUE #(
( %cid = 'BOOK_1"
Title = 'Test"
Isbn = '12345' ) " Trop court !
).
" When : Créer le livre
MODIFY ENTITIES OF zi_book
ENTITY Book
CREATE FROM lt_create
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES
RESPONSE OF zi_book
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
" Then : Une erreur devrait survenir
cl_abap_unit_assert=>assert_not_initial(
act = commit_failed-book
msg = 'La validation devrait échouer pour un ISBN invalide"
).
" Vérifier le message d'erreur
cl_abap_unit_assert=>assert_bound(
act = commit_reported-book[ 1 ]-%msg
msg = 'Un message d''erreur devrait être présent"
).
ENDMETHOD.
METHOD test_validate_pages_zero.
" Given : Livre avec 0 pages
DATA lt_create TYPE TABLE FOR CREATE zi_book.
lt_create = VALUE #(
( %cid = 'BOOK_1"
Title = 'Test"
Pages = 0 ) " Invalide !
).
" When : Création
MODIFY ENTITIES OF zi_book
ENTITY Book
CREATE FROM lt_create.
COMMIT ENTITIES
FAILED DATA(failed).
" Then : Erreur
cl_abap_unit_assert=>assert_not_initial(
act = failed-book
msg = 'La validation des pages devrait échouer"
).
ENDMETHOD.
ENDCLASS.

Exécuter les tests

Dans ADT :

  1. Clic droit sur ZBP_I_BOOK
  2. Run As -> ABAP Unit Test
  3. Résultats dans l’onglet ABAP Unit

Tous les tests sont verts ? Félicitations !


Gestion des erreurs et Logging

Messages d’erreur clairs

MAUVAIS :

APPEND VALUE #(
%tky = ls_book-%tky
%msg = new_message_with_text(
text = 'Erreur' " Pas utile !
)
) TO reported-book.

MIEUX :

APPEND VALUE #(
%tky = ls_book-%tky
%element-Isbn = if_abap_behv=>mk-on " Marquer le champ
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = |L'ISBN { ls_book-Isbn } est invalide : doit avoir 10 ou 13 caractères|
)
%path-Book-%is_draft = ls_book-%is_draft " Contexte Draft
) TO reported-book.

Utiliser les messages T100 (Production)

Créer une classe de messages :

  1. SE91 -> Créer la Message Class ZBOOK_MSG
  2. Message 001 : L'ISBN &1 est invalide

Utiliser dans le code :

APPEND VALUE #(
%tky = ls_book-%tky
%element-Isbn = if_abap_behv=>mk-on
%msg = new_message(
id = 'ZBOOK_MSG"
number = '001"
severity = if_abap_behv_message=>severity-error
v1 = ls_book-Isbn
)
) TO reported-book.

Avantage : Multilingue, gestion centralisée


Application Logging

Pour les opérations importantes (ex. modifications en masse) :

METHOD markAsFinished.
" ... Logique de l'action ...
" Logging
TRY.
DATA(lo_log) = cl_bali_log=>create_with_header(
header = cl_bali_header_setter=>create(
object = 'ZBOOK"
subobject = 'STATUS"
external_id = |Book { ls_book-BookId }|
)
).
lo_log->add_item(
item = cl_bali_free_text_setter=>create(
severity = if_bali_constants=>c_severity_information
text = |Livre { ls_book-BookId } marqué comme terminé|
)
).
cl_bali_log_db=>get_instance( )->save_log(
log = lo_log
).
CATCH cx_bali_runtime INTO DATA(lx_error).
" Ignorer les erreurs de logging (non critique)
ENDTRY.
ENDMETHOD.

Voir les logs : Transaction SLG1 ou application Fiori “Application Logs”


Bonnes pratiques de déploiement

1. Organisation des transports

Structure des packages :

ZBOOK (Package principal)
├── ZBOOK_MODEL (CDS Views, Tables)
├── ZBOOK_BEHAVIOR (BDEFs, BILs)
├── ZBOOK_SERVICE (Service Definitions, Bindings)
└── ZBOOK_UI (Metadata Extensions)

Avantage : Séparation claire, transports sélectifs

2. Conventions de nommage

Type d’objetPréfixeExemple
TableZ<APP>_TABZBOOK_TAB
CDS InterfaceZI_<Entity>ZI_BOOK
CDS ProjectionZC_<Entity>ZC_BOOK
BDEF ImplementationZBP_I_<Entity>ZBP_I_BOOK
Service DefinitionZUI_<Entity>_O4ZUI_BOOK_O4
Service BindingZUI_<Entity>_O4ZUI_BOOK_O4

La cohérence est importante !

3. Versioning avec abapGit

Configuration :

  1. Créer un repo GitHub
  2. Installer le plugin abapGit dans ADT
  3. Lier le package au repo

Workflow :

Terminal window
# Modifications locales
git add .
git commit -m "feat: Add ISBN validation"
git push origin main
# Dans un autre système
git pull origin main
# Import via abapGit

Checklist de revue de code

Vérifier avant chaque transport :

Fonctionnalité

  • Toutes les opérations CRUD fonctionnent
  • Les Actions exécutent les opérations attendues
  • Les Validations vérifient correctement
  • Les Determinations définissent les valeurs par défaut

Performance

  • Pas de SELECT dans les boucles
  • Opérations EML regroupées
  • CDS Views optimisées (pas de JOINs inutiles)
  • SQL Trace effectué (pas de requêtes lentes)

Qualité du code

  • Mode Strict activé (strict ( 2 ))
  • Pas d’erreurs ATC (Warnings acceptables)
  • Commentaires pour la logique complexe
  • Conventions de nommage respectées

Gestion des erreurs

  • Toutes les Validations ont des messages explicites
  • FAILED et REPORTED correctement remplis
  • Messages T100 pour les messages de production

Tests

  • Tests unitaires présents (>70% de couverture pour la logique critique)
  • Tous les tests sont verts
  • Tests end-to-end manuels effectués

Sécurité

  • Contrôles d’autorisation présents
  • Pas de credentials en dur
  • Validation des entrées (prévention XSS, SQL Injection)

Documentation

  • README.md dans le repo abapGit
  • Logique complexe commentée
  • Documentation API (si APIs publiques)

Éviter les erreurs courantes

Erreur 1 : Oublier COMMIT ENTITIES

Problème :

MODIFY ENTITIES OF zi_book
ENTITY Book CREATE FROM lt_books.
" Pas de COMMIT - les données sont perdues !

Solution :

MODIFY ENTITIES OF zi_book
ENTITY Book CREATE FROM lt_books
FAILED DATA(failed)
REPORTED DATA(reported).
COMMIT ENTITIES. " TOUJOURS !

Mais : Dans les Behavior Implementations, PAS de COMMIT (le framework s’en charge) !


Erreur 2 : Table Expressions sans gestion des exceptions

Problème :

DATA(ls_book) = lt_books[ BookId = lv_id ]. " Shortdump si non trouvé !

Solution :

TRY.
DATA(ls_book) = lt_books[ BookId = lv_id ].
CATCH cx_sy_itab_line_not_found.
" Gestion de l'erreur
ENDTRY.
" Ou : OPTIONAL
DATA(ls_book) = VALUE #( lt_books[ BookId = lv_id ] OPTIONAL ).

Erreur 3 : Feature Control non implémenté

Sans Feature Control, toutes les Actions sont toujours disponibles - même si ça n’a pas de sens.

Exemple : “Marquer comme terminé” pour un livre déjà terminé.

Solution : Toujours implémenter get_instance_features !


Erreur 4 : Validations trop tardives

Problème :

validation validateIsbn on modify { ... } " Trop tard - données déjà dans l'UI !

Solution :

validation validateIsbn on save { ... } " Vérifier avant la sauvegarde

Encore mieux : Validation côté client via Annotations (pour un feedback immédiat)


Benchmarks de performance

Notre système Book Management :

OpérationSans optimisationAvec optimisationAmélioration
Créer 100 livres2.5s0.3s88%
Lire 1000 livres1.2s0.2s83%
Validation ISBN (100 livres)5.0s0.5s90%

Conclusion : L’optimisation des performances en vaut la peine !


Résumé : Ce que nous avons appris

Performance

  • Éviter SELECT dans les boucles -> FOR ALL ENTRIES
  • Regrouper les opérations EML -> Un MODIFY pour tous
  • Optimiser les CDS Views -> Index, moins de JOINs

Tests

  • Tests unitaires avec cl_cds_test_environment
  • Couverture de test >70% pour la logique critique
  • Pattern Given-When-Then

Gestion des erreurs

  • Messages d’erreur explicites
  • Messages T100 pour la production
  • Application Logging pour l’audit

Déploiement

  • Réfléchir à la structure des packages
  • Respecter les conventions de nommage
  • abapGit pour le versioning

Qualité du code

  • Suivre la checklist de revue de code
  • Checks ATC sans erreurs
  • Éviter les erreurs courantes

Votre parcours RAP continue

Vous avez maintenant :

  • Une application Fiori entièrement fonctionnelle
  • Une logique métier (Actions, Validations)
  • Un code prêt pour la production (Performance, Tests)
  • Les bonnes pratiques apprises

Prochaines étapes :

  1. Créer votre propre application : Utilisez ce que vous avez appris pour un projet personnel
  2. Étendre : Ajoutez Associations, Draft, Side Effects
  3. Approfondir : Apprenez RAP avancé (Compositions, Héritage)
  4. Certification : Certifications ABAP Cloud

Ressources complémentaires

Série de tutoriels RAP :

Autres articles :

Ressources SAP :


Retours ?

  • Cette série de tutoriels vous a-t-elle aidé ?
  • Quel sujet souhaiteriez-vous approfondir ?
  • Vous créez votre propre application RAP ? Partagez-la !

Bonne chance avec ABAP Cloud et RAP !