Automation QA (Assurance Qualité)Ingénieur QA d'Automatisation Senior / Architecte QA

Élaborez un plan pour un cadre d'automatisation capable d'exécuter la validation des processus métier de bout en bout à travers des piles technologiques hétérogènes — en orchestrant spécifiquement les interactions entre les émulateurs de terminal mainframe legacy (3270), les APIs REST modernes et les portails web basés sur React dynamiques — tout en maintenant un langage spécifique au domaine (DSL) unifié pour les analystes commerciaux et en assurant des capacités de retour en arrière pour l'intégrité transactionnelle à travers ces systèmes disparates ?

Réussissez les entretiens avec l'assistant IA Hintsage

Historique de la question

Les entreprises qui subissent une transformation numérique opèrent souvent dans des environnements complexes « brownfield » où les transactions essentielles des banques continuent d'être traitées par des mainframes COBOL âgés de plusieurs décennies sur des systèmes IBM z/OS. Simultanément, les flux de service et d'onboarding orientés client sont de plus en plus gérés par des portails web modernes basés sur React et des applications mobiles. Cette divergence technologique crée un défi de validation significatif pour les équipes QA, qui doivent garantir un flux de données transparent et sans erreur ainsi qu'une cohérence transactionnelle à travers ces architectures fondamentalement disparates.

Traditionnellement, les efforts d'automatisation dans de tels environnements deviennent très cloisonnés, avec des équipes spécialisées maintenant des ensembles d'outils séparés pour l'émulation de terminal mainframe (comme Jagacy ou Extra !), l'automatisation générique de l'UI (Selenium ou Cypress), et la validation des APIs (Rest-Assured ou Postman). Cette fragmentation entraîne des suites d'intégration fragiles écrites dans un jargon hautement technique que les analystes commerciaux non techniques ne peuvent ni examiner, ni valider par rapport aux exigences. De plus, des problèmes catastrophiques d'intégrité des données émergent fréquemment lorsqu'un test échoue en cours d'exécution, laissant potentiellement un compte mainframe créé alors que la vérification sur le portail web correspondant reste incomplète, polluant ainsi les environnements en aval avec des données de test orphelines.

Cette question spécifique a émergé d'une entreprise de services financiers Fortune 500 tentant de valider un workflow complexe d'« onboarding de nouveaux clients » qui s'étendait sur une application mobile React, un bus d'événements Kafka, une couche de microservices Java et la provision finale de comptes sur un mainframe IBM z/OS. L'organisation nécessitait une stratégie d'automatisation unifiée capable de combler ces écarts techniques tout en maintenant l'agilité attendue dans les pipelines DevOps modernes. Le défi était encore compliqué par la nécessité pour les analystes commerciaux d'écrire et de comprendre des scénarios de test sans comprendre les mises en œuvre techniques sous-jacentes de chaque système.

Le problème

Le principal défi réside dans le décalage fondamental entre l'automatisation web synchrone, qui s'attend à des mises à jour immédiates du DOM et à des interactions pilotées par des événements, et l'émulation de terminal en mode bloc des mainframes 3270, qui repose sur un scraping d'écran explicite et un positionnement précis du curseur. Les APIs REST introduisent une complexité supplémentaire en fonctionnant dans un paradigme de requête-réponse sans état qui manque de la continuité de session inhérente aux sessions de terminal. Combler ces styles architecturaux nécessite une couche d'abstraction capable de traduire des actions métiers de haut niveau en commandes spécifiques au système sans divulguer de détails d'implémentation technique dans les scénarios de test.

Maintenir un langage spécifique au domaine (DSL) unifié en utilisant des outils comme Gherkin devient extrêmement difficile lorsque les mises en œuvre techniques des étapes de test divergent de manière si radicale à travers les systèmes. Les éléments web sont typiquement identifiés à l'aide de sélecteurs CSS ou d'expressions XPath, les validations d'API reposent sur des assertions de chemin JSON et la validation de schéma, tandis que les interactions mainframe dépendent des coordonnées des champs, des étiquettes d'écran ou de séquences de touches spécifiques comme F1 ou Entrée. Sans une stratégie d'abstraction robuste, le DSL devient rapidement encombré de localisateurs techniques et de jargon spécifique au système, contrecarrant son objectif en tant que moyen de communication entre les parties prenantes commerciales et techniques.

En outre, garantir une véritable intégrité transactionnelle à travers ces systèmes distribués nécessite la mise en œuvre d'un modèle de Saga ou de Transaction Compensatoire directement au sein de l'architecture du cadre de test, ce qui n'est pas trivial lorsque la couche de test manque de crochets natifs dans les protocoles de validation à deux phases du mainframe ou dans les gestionnaires de transactions distribuées. Lorsqu'un échec de test se produit sur le portail web après qu'une transaction mainframe ait déjà été engagée, le cadre doit posséder l'intelligence et la capacité de déclencher des procédures de retour explicites pour restaurer la cohérence de l'environnement. Cela nécessite des mécanismes sophistiqués de suivi des états et de gestion des erreurs qui vont bien au-delà des blocs standard de try-catch.

Enfin, le cadre d'automatisation doit gérer en toute sécurité des mécanismes d'authentification disparates sans intégrer directement des identifiants sensibles dans les scripts de test. Les portails web utilisent souvent des flux modernes OAuth2 ou SAML avec authentification à plusieurs facteurs (MFA), les APIs REST reposent sur des clés API ou des tokens JWT, tandis que les mainframes legacy s'authentifient auprès des fournisseurs RACF ou ACF2 utilisant des profils utilisateurs statiques. Un coffre-fort de credentials centralisé et chiffré avec des capacités d'injection spécifiques à l'environnement est essentiel pour maintenir la posture de sécurité tout en permettant une authentification fluide entre les systèmes.

La solution

Pour aborder ces complexités, le cadre devrait être conçu en utilisant le modèle d'Architecture Hexagonale (Ports et Adaptateurs), qui impose une séparation stricte entre la logique métier du test et les interactions avec les systèmes externes. Définir une interface port abstraite ApplicationDriver déclarant des méthodes de domaine de haut niveau telles que enterCustomerData(), verifyAccountCreation() et rollbackTransaction(). Cette interface agit comme le seul contrat avec lequel votre couche DSL (telles que les définitions d'étapes Cucumber ou les liaisons SpecFlow) est autorisée à interagir, garantissant une isolation complète par rapport aux spécificités de l'implémentation.

Les implémentations d'adaptateurs concrets gèrent les spécificités techniques propres au système : un SeleniumWebAdapter traduit les méthodes de port en interactions de navigateur, un RestAssuredAdapter exécute des appels HTTP et analyse des réponses JSON, et un HllapiMainframeAdapter utilise l'API de Langage de Haut Niveau pour envoyer des touches, lire des buffers d'écran et valider le contenu des champs sur l'émulateur 3270. Chaque adaptateur encapsule sa propre logique de réessai, des mécanismes d'attente explicites et des stratégies de gestion des erreurs appropriées à sa pile technologique. Lorsqu'un adaptateur termine avec succès une action qui modifie l'état, il publie un événement de domaine (tel que AccountCreatedEvent) à un TestEventBus central plutôt que de retourner des types de données primitifs.

Pour l'intégrité transactionnelle, implémentez un Orchestrateur de Saga de Test qui maintient un journal ordonné de toutes les actions CompensableAction exécutées durant un scénario de test. Si une étape dans le workflow échoue avec une exception, l'orchestrateur exécute automatiquement la méthode compensate() des actions précédemment réussies dans l'ordre inverse, exécutant efficacement une transaction compensatoire pour supprimer le compte mainframe ou annuler la réservation API. Ce modèle garantit que l'environnement de test reste propre même lorsque les tests échouent en cours de route, empêchant l'accumulation de données orphelines qui affecte les suites de bout en bout traditionnelles.

La gestion des états à travers la pile hétérogène est réalisée en traitant le TestContext comme un citoyen de première classe, en utilisant ThreadLocal<DomainContext> pour stocker des objets de domaine riches au lieu de chaînes primitives, évitant ainsi un couplage étroit entre les étapes de test. L'adaptateur React pourrait peupler un objet CustomerProfile dans le contexte, que l'adaptateur mainframe récupère ensuite pour exécuter sa partie du workflow. Cette approche garantit que le DSL reste concentré sur les entités commerciales plutôt que sur des identifiants techniques comme des IDs de session ou des coordonnées d'écran.

Pour lier ces composants ensemble, utilisez un bus de messagerie léger tel que Google Guava EventBus ou un flux réactif permettant aux adaptateurs de communiquer des changements d'état sans invocation de méthode directe, dé-couplant ainsi le flux mainframe du flux de validation web. Lorsque le HllapiMainframeAdapter crée avec succès un compte, il publie un événement contenant les détails du compte, que le SeleniumWebAdapter consomme pour naviguer automatiquement vers l'écran de vérification approprié. Cette approche pilotée par événements au sein du cadre de test imite l'architecture moderne des microservices et réduit considérablement les charges de maintenance lorsque les interfaces de système individuelles changent.

// Définition de l'interface Port public interface BankingDriver { void enterCustomerData(Customer customer); AccountDetails submitAccountCreation(); void verifyAccountInPortal(AccountDetails account); void rollbackAccountCreation(AccountDetails account); } // Adaptateur Mainframe utilisant HLLAPI public class MainframeAdapter implements BankingDriver { private final HllapiWrapper hllapi; private final EventBus eventBus; @Override public AccountDetails submitAccountCreation() { hllapi.sendKey("@E"); // Simuler la touche Entrée waitForScreen("Compte Créé"); String accountId = hllapi.getTextByLabel("Numéro de Compte :"); AccountDetails details = new AccountDetails(accountId); eventBus.post(new AccountCreatedEvent(details)); return details; } @Override public void rollbackAccountCreation(AccountDetails account) { hllapi.sendKeys("SUPPRIMER " + account.getId()); hllapi.sendKey("@E"); verifyScreen("Suppression Confirmée"); } } // Orchestrateur de Saga pour l'intégrité transactionnelle public class TestSagaOrchestrator { private final List<CompensableAction> executedActions = new ArrayList<>(); public void execute(Runnable action, Runnable compensation) { try { action.run(); executedActions.add(new CompensableAction(action, compensation)); } catch (Exception e) { compensate(); throw new TestFailureException(e); } } private void compensate() { Collections.reverse(executedActions); for (CompensableAction action : executedActions) { try { action.compensate(); } catch (Exception ex) { publishToDeadLetterQueue(action, ex); } } } }

Situation vécue

Lors d'un engagement de conseil en 2022 avec un fournisseur d'assurance mondial en cours de transformation numérique, j'ai rencontré un processus métier critique appelé "Premier Avis de Sinistre" (FNOL) qui illustre exactement ces défis. Le workflow nécessitait qu'un titulaire de police soumette une réclamation via une application mobile React Native en téléchargeant des photos d'accident, ce qui déclenchait un microservice Python basé sur l'apprentissage automatique pour l'évaluation des dommages et la détection de fraudes, avant de finalement mettre à jour un système mainframe Unisys legacy pour allouer des réserves financières et valider la couverture de police. La stratégie d'automatisation existante reposait sur trois suites distinctes et non communicantes : Cypress pour l'application mobile, Pytest pour l'API, et Jagacy pour l'émulation de terminal mainframe.

L'approche cloisonnée nécessitait une corrélation manuelle des numéros de réclamation entre les équipes utilisant des feuilles Excel partagées, et la pollution de l'environnement est devenue un obstacle majeur pendant les cycles de régression. Le moment de crise est survenu lorsqu'un délai d'expiration du réseau mobile a causé un échec de test après que le mainframe avait déjà engagé une allocation de réserve de 50 000 $, laissant les données financières dans un état incohérent nécessitant quatre heures de nettoyage manuel par un programmeur systèmes mainframe. Cet incident a directement violé la politique de « propre environnement » de l'équipe et a bloqué le pipeline CI/CD pendant toute une journée de travail.

Nous avons évalué trois stratégies potentielles de remédiation pour éviter que cela ne se reproduise à l'avenir. La première option consistait à écrire des scripts de nettoyage de base de données post-test pour inverser manuellement les transactions mainframe, mais cela a été rejeté en raison de politiques de sécurité interdisant l'accès SQL direct à l'environnement mainframe UAT semblable à la production. La deuxième approche proposait de mettre en œuvre un pool de données de test partagé avec des mécanismes de verrouillage pessimistes pour sérialiser l'exécution des tests, mais cela aurait augmenté le temps d'exécution de la suite de vingt minutes à plus de quatre heures, annulant complètement les avantages de la parallélisation dans CI/CD. La troisième stratégie, que nous avons finalement sélectionnée, impliquait de mettre en œuvre un modèle de Saga au sein du cadre d'automatisation de test lui-même, imitant le modèle de cohérence éventuelle de l'application tout en préservant la possibilité d'exécuter des centaines de tests en parallèle.

La solution mise en œuvre a introduit un orchestrateur ClaimSaga qui interceptait chaque action effectuée par les adaptateurs mobile et mainframe. Lorsque l'adaptateur mobile lançait une StaleElementReferenceException en raison du délai d'expiration du réseau, la saga déclenchait immédiatement la transaction compensatoire reverseReserveAllocation() sur l'adaptateur mainframe en utilisant l'ID de réclamation stocké dans le contexte ThreadLocal. Ce mécanisme automatique de retour en arrière a réduit la pollution de données environnementales de quatre-vingt-dix-huit pour cent et a permis à l'équipe d'exécuter en toute confiance cinq cents fils parallèles dans leur pipeline Jenkins sans craindre de créer des dossiers financiers orphelins.

Cette amélioration dramatique de la fiabilité des tests a permis à l'équipe QA de réorienter son attention de la purification manuelle des données vers des tests exploratoires et l'analyse des cas limites. Les analystes commerciaux pouvaient enfin rédiger et examiner des scénarios de test rédigés en anglais simple, tels que Étant donné qu'un titulaire de police signale un accident majeur, lorsque les photos sont téléchargées mais que le service d'évaluation AI dépasse les délais, alors aucune réserve financière ne sera allouée. Cela garantissait que la suite d'automatisation servait de documentation vivante précise reflétant des règles commerciales complexes à travers les trois niveaux technologiques.

Ce que les candidats oublient souvent


Comment gérez-vous la persistance de l'état de session à travers l'émulateur et le portail web sans créer un couplage étroit entre les adaptateurs ?

Les candidats novices tentent souvent de résoudre ce problème en retournant des identifiants de session bruts ou des clés primaires de base de données directement à partir des méthodes de définition d'étapes, créant des dépendances fragiles où l'Étape B ne peut pas s'exécuter tant que l'Étape A n'a pas explicitement retourné une valeur de chaîne spécifique. Cette approche rompt fondamentalement les principes de conception pilotée par le domaine et force les étapes Gherkin lisibles par les affaires à être ordonnées dans une séquence strictement technique plutôt que dans un flux commercial logique. De plus, cela expose des détails d'implémentation dans la couche DSL, rendant les tests fragiles lorsque les identifiants techniques changent de format.

La solution architecturale robuste met en œuvre un Contexte de Scénario ou Contexte des Données de Test qui sert de registre transitoire pendant la durée de l'exécution du test, généralement implémenté en utilisant ThreadLocal<Map<Class<?>, Object>> pour garantir la sécurité des threads lors de l'exécution parallèle. Les adaptateurs ne retournent pas de valeurs primitives à la couche DSL ; au lieu de cela, ils publient des événements ou objets de domaine fortement typés dans ce contexte. Par exemple, lorsque l'adaptateur mainframe crée avec succès un compte, il publie un AccountCreatedEvent contenant l'entité complète du compte, que l'adaptateur web récupère ensuite en écoutant le bus d'événements ou en interrogeant le contexte.

Cette approche pilotée par événements garantit que la couche DSL reste totalement indifférente à l'origine des données, que le numéro de police ait été extrait d'un écran vert ou retourné dans une réponse JSON. En se basant sur des abstractions plutôt que sur des implémentations concrètes, le cadre respecte le principe d'inversion des dépendances. Cela permet aux adaptateurs individuels d'être refondus ou remplacés sans impacter les scénarios de test lisibles par les affaires, réduisant considérablement les coûts de maintenance à long terme.


Quel mécanisme spécifique empêche une transaction compensatoire d'échouer elle-même, laissant potentiellement le système dans un état incohérent ?

De nombreux ingénieurs juniors négligent le mode d'échec critique où la logique de compensation elle-même rencontre une erreur. De telles erreurs peuvent inclure des délais d'expiration réseau lors de la tentative de suppression d'un enregistrement mainframe ou des échecs de validation parce que l'enregistrement a déjà été modifié par un processus d'arrière-plan concurrent. Ce scénario entraîne une accumulation de « données toxiques » où l'action originale a réussi mais le retour en arrière a échoué, laissant l'environnement de test dans un état corrompu de manière permanente.

La solution nécessite la mise en œuvre d'actions compensatoires idempotentes qui sont conçues pour être réessayées de manière sécurisée plusieurs fois sans provoquer d'erreurs de suppression en double. Celles-ci devraient être couplées à un mécanisme de réessai robuste aux délais d'expiration exponentiels et aux modèles de disjonction pour gérer gracieusement les pannes d'infrastructure transitoires. Si toutes les tentatives de réessai échouent, le cadre doit publier les détails de compensation échoués dans une Dead-Letter Queue (DLQ) persistante. Cette DLQ peut être implémentée comme une table de base de données ou un topic de message contenant des ID de corrélation complets et des traces de pile.

De plus, mettez en œuvre des portes de validation avant d'essayer la compensation pour vérifier l'état actuel du système en aval. Par exemple, validez que le compte mainframe existe, a un solde nul et ne présente pas de modifications utilisateur récentes avant d'émettre une commande de suppression. Un travail de réconciliation automatisé nocturne peut ensuite traiter la DLQ pour gérer ces enregistrements orphelins manuellement, assurant que l'environnement de test s'auto-répare et prévenant les régressions critiques masquées par la pollution de données existantes.


Pourquoi l'utilisation de l'extraction d'écran basée sur des coordonnées (HLLAPI) pour les mainframes est-elle considérée comme une responsabilité, et comment l'abstrayez-vous pour réduire les charges de maintenance lorsque les mises en page d'écran changent inévitablement ?

Les candidats plaident souvent pour des coordonnées de ligne et de colonne codées en dur, telles que getText(10, 45, 10) pour lire dix caractères à partir de la ligne dix, colonne quarante-cinq. Ils préfèrent cette approche car elle semble précise et déterministe lors du développement initial des tests. Cependant, cette stratégie crée une charge de maintenance sévère, car les applications mainframe subissent fréquemment des modifications d'écran où de nouveaux champs sont insérés, déplaçant ainsi tous les décalages de coordonnées ultérieurs et rendant l'ensemble des suites de tests invalides sans avertissement.

La solution architecturale robuste met en œuvre un Modèle d'Objet d'Écran qui mappe des noms de champs logiques (tels que ACCOUNT_NUMBER_FIELD) à des critères de recherche dynamiques plutôt qu'à des coordonnées statiques. Il utilise les capacités d'Identification de Champ de l'émulateur mainframe, disponibles via des fonctions HLLAPI comme FindFieldPosition ou SearchField, pour localiser les champs par leurs étiquettes associées (par exemple, rechercher le texte "Numéro de Compte :"). Au moment de l'exécution, l'adaptateur recherche le texte d'étiquette dans le tampon d'écran et calcule le décalage relatif vers le champ de saisie correspondant. Lorsque la mise en page de l'écran change, seul le fichier de configuration JSON mappant les étiquettes aux décalages nécessite une mise à jour, laissant le code Java compilé intact.

Pour une résilience encore plus grande, mettez en œuvre un mécanisme de Hash d'Écran ou Checksum capturant un hachage cryptographique des contenus de champ non protégés au début de l'interaction. Si le hachage ne correspond pas à la référence attendue, le cadre échoue rapidement avec une claire erreur de « Mismatch d'Écran » au lieu de tenter de lire des données à partir de positions incorrectes. Cela empêche les tests de se poursuive avec des données corrompues qui généreraient de fausses négatives ou de fausses positives et alerte immédiatement l'équipe d'automatisation sur les changements d'écran nécessitant des mises à jour de configuration.