15 december 2023
Utilisation des modèles YAML dans Azure DevOps pour des actions répétables : Partie 1 - Un peu sur le test automatisé des applications
Initialement, j'avais l'intention d'écrire un article sur la pratique de l'utilisation des modèles YAML dans Azure DevOps pour encapsuler des étapes répétables dans les pipelines. Mais pendant le processus d'écriture, j'ai réalisé que de simples références à la documentation et des exemples de fichiers YAML ne seraient pas aussi intéressants sans les relier à un scénario. Ainsi, un court article purement technique s'est transformé en un texte volumineux dédié à plusieurs sujets, chacun étant intéressant en soi. En fin de compte, j'ai décidé de transformer ce texte en une mini-série d'articles. Bienvenue dans la première partie, dédiée au test automatisé, où je donnerai un bref aperçu de l'approche du test logiciel et décrirai un scénario que nous automatiserons à l'aide de pipelines YAML avec des modèles dans le prochain article.
La théorie des tests est une science séparée, importante et très intéressante dans le domaine de la technologie de l'information. Les professionnels du test sont aussi précieux dans une équipe que les développeurs, les DevOps, les chefs de projet et d'autres rôles. Ils sont responsables de garantir la qualité des produits logiciels et des systèmes.

Si vous souhaitez plonger dans les détails de cette théorie, vous pouvez commencer par ce lien. Là-bas, les tests sont assez bien décrits, ce que c'est, les types qu'ils peuvent être, et ainsi de suite. Mais dans cet article, je vais essayer d'expliquer brièvement l'essence du test et d'expliquer ce à quoi nous aspirons pour garantir la qualité de notre produit logiciel hypothétique.

Alors, nous savons tous que les logiciels contiennent des bogues. Et moins il y a de bogues, mieux c'est. Mais qu'est-ce qu'un bogue, et que signifie pour un système d'être exempt d'erreurs ? Pour moi, j'ai dérivé la définition suivante.

Un système est exempt d'erreurs et de bogues s'il:

  • d'une part, se comporte globalement exactement comme les auteurs du système l'ont prévu ;
  • d'autre part, en détail, se comporte comme attendu par l'utilisateur.

En d'autres termes, les fonctionnalités (logique métier) fonctionnent comme les auteurs le veulent, et l'interface utilisateur et les petits détails de l'interaction avec l'utilisateur fonctionnent comme l'utilisateur s'y attend. Cela ne doit pas être confondu avec la conception UX. Je veux dire des choses de base, comme le fait que le fait de cliquer sur le bouton "ouvrir le fichier" ouvre une boîte de dialogue avec la sélection de fichiers, au bon endroit sur l'écran, avec les bons noms et filtres.

Maintenant, la question se pose, comment garantir que le système contient le moins de bogues possible ? Bien sûr, en le testant. Mais comment tester, quand tester ? Commençons par la question "quand". J'aime beaucoup l'approche appelée Shift left.
(pris d'ici)
C'est très simple : testez autant que possible et le plus tôt possible. Pourquoi ? Parce que le "coût" de la correction d'un bogue augmente considérablement avec le temps où il existe.
Lorsqu'un développeur vérifie le code qu'il écrit, les erreurs sont corrigées très facilement. La création de tests unitaires nécessite un effort supplémentaire, mais corriger les erreurs après l'exécution de ces tests ne "coûte" pas non plus beaucoup (généralement l'auteur du code écrit des tests unitaires tout en étant encore dans le contexte de la tâche). Mais déjà au stade des tests fonctionnels et système, le "coût" commence à augmenter considérablement, car d'autres personnes sont impliquées dans la recherche de tels bogues, des frameworks spéciaux sont déployés, le déploiement de l'application est effectué, et ainsi de suite. Tout cela prend du temps et de l'argent. Eh bien, corriger un bogue qui est passé en version finale peut être extrêmement coûteux, car il peut affecter directement les processus métier et potentiellement priver l'entreprise de certaines quantités de profit.

Dans mon esprit, je divise le processus de détection des erreurs en deux grandes étapes:

  • les tests écrits par les développeurs
  • les tests écrits par les équipes QA (assurance qualité)
Généralement, en plus de tester le code pendant son processus d'écriture, les développeurs sont également responsables des tests unitaires. Les tests qui vérifient les modules, méthodes, fonctions et procédures de bas niveau nécessitent généralement une plongée profonde dans le contexte du code, et si ces tests ne sont pas écrits pendant ou immédiatement après le développement, la complexité et les efforts nécessaires pour écrire de tels tests augmentent considérablement. Cela est dû au fait qu'après plusieurs jours, le développeur devra à nouveau se plonger dans le code, et quelqu'un qui n'a pas écrit le code devra également l'étudier pour comprendre comment le tester.

Par conséquent, je conclus que ce sont les développeurs qui doivent écrire les tests unitaires. Et si ces tests ne sont pas écrits immédiatement, il vaut mieux ne pas les écrire du tout et se concentrer sur les tests à un niveau supérieur. À mon avis, cette approche serait beaucoup plus efficace dans cette situation.

Ensuite, l'équipe d'Assurance Qualité (QA) entre en jeu. Leur travail diffère considérablement des tests écrits par les développeurs. Ils testent l'interaction de haut niveau des modules, la fonctionnalité (logique métier) de l'application et l'interaction avec l'utilisateur, mais n'effectuent pas de tests de code de bas niveau pour les raisons que j'ai évoquées ci-dessus. Cette équipe comprend à la fois des testeurs manuels et des personnes impliquées dans l'automatisation des tests. Qui exactement est responsable de l'intégration dans les pipelines CI est une question organisationnelle. Dans notre cas, supposons qu'il s'agisse d'un ingénieur DevOps responsable de l'ensemble du pipeline d'automatisation des tests.

L'équipe de test manuel étudie la documentation et la fonctionnalité de notre produit logiciel et rédige des scénarios étape par étape pour tester correctement le système. Ces scénarios sont ensuite utilisés à la fois pour les tests manuels et pour l'automatisation.

Une partie de l'équipe responsable de l'automatisation crée des tests, que je divise également en deux parties:

  • Tests développeur
  • Tests système

Veuillez noter que le terme "test système" ici a une signification légèrement différente de celle généralement comprise dans la théorie du test.

Les tests développeur sont des tests qui fonctionnent dans une partie isolée de l'application. Techniquement, ils sont les mêmes que les tests unitaires, créés selon les mêmes règles et en utilisant les mêmes frameworks, mais ils testent non pas le code de module de bas niveau mais l'interaction de plus haut niveau au sein de l'application (tests d'intégration) et, si possible, la correction de la fonctionnalité principale du système (tests fonctionnels). Essentiellement, dans le code, on peut émuler une demande utilisateur spécifique et vérifier comment le système y répond. Juste sans utiliser l'interface utilisateur. Ces tests, comme les tests unitaires, peuvent être exécutés sans déployer une installation complète du système. Ils sont généralement très rapides et ne nécessitent pas de ressources spéciales pour s'exécuter.

Les tests système sont tous les tests qui nécessitent un déploiement complet de l'instance du système. Cela comprend les tests API, les tests d'interface utilisateur, les tests de charge, et autres. Ils utilisent généralement des frameworks spéciaux et nécessitent plus de temps pour s'exécuter. Par conséquent, le "coût" d'exécution de tels tests est plus élevé.
En fin de compte, nous obtenons deux catégories de tests :
  • rapides et "bon marché" mais plus superficiels et synthétiques
  • lents et "chers" mais plus profonds et complets

En suivant le principe de Pareto, nous pouvons nous attendre à ce que les tests rapides nous aident à détecter environ 80 % des bogues, tandis que les tests lents nous aideront à trouver les autres.

Et en fonction de cette logique, pour améliorer considérablement la qualité de notre logiciel, il suffit de lancer les tests rapides, par exemple, avec chaque demande de tirage, tandis que l'exécution des tests lents est moins fréquente et dans des cas spéciaux (par exemple, lors de la préparation des versions).

Et en fin de compte, nous obtenons le schéma suivant

Les testeurs "manuels" rédigent des scénarios de test qui sont automatisés à la fois pour les tests "système" et les tests "développeur". Les deux types de tests sont enrichis d'intégration et d'autres types de tests selon le jugement de l'équipe et intégrés dans les pipelines CI. Nous lançons les tests développeur aussi souvent que possible, par exemple, au niveau de la demande de tirage. Cela nous permet de détecter la plupart des erreurs aux premiers stades, lorsque les développeurs travaillent sur les demandes de tirage. Les tests système ne sont exécutés que lorsque l'ensemble du système est construit, par exemple, avant toute mise en production, et ils aident à identifier des bugs de niveau supérieur (comme ceux liés à l'interface utilisateur ou à l'environnement système).

C'est précisément pour cette approche que nous écrirons un pipeline YAML pour Azure DevOps dans le prochain article.