Je travaille actuellement sur un projet qui vise à implémenter le test automatique d'un progiciel. Vous pouvez imaginer que ce logiciel est un peu comme Excel en ce qu'il a un espace de travail qui contient toutes les données et une interface utilisateur qui exécute du code qui fonctionne sur ces données de l'espace de travail. Nous nous concentrons principalement sur le test des "fonctionnalités de base", plutôt que sur l'interface utilisateur en elle-même. Auparavant, de nombreux tests étaient manuels et mal documentés.
L'un des problèmes auxquels nous sommes confrontés est que pratiquement tout ce que vous pourriez vouloir tester nécessite un ensemble de données "complet". En raison de complications architecturales héritées, il n'est pas vraiment possible de tester quoi que ce soit - la moquerie requise est trop étendue.
Un autre problème auquel nous sommes confrontés est que le code est dans plusieurs langages différents - principalement Python et C++. Une partie du code Python est en cours d'exécution dans a = Python interprète fonctionnant dans le logiciel, lui donnant accès au code C++ qui est autrement moins accessible.
De plus, le niveau actuel des tests manuels est jugé "assez bon", et nous visons à obtenir au moins "la même couverture". Cependant, étant donné que les tests manuels passent inévitablement par l'interface utilisateur et que les cas manuels actuels sont extrêmement mal documentés, qui sait ce que nous couvrons actuellement avec les tests manuels?
J'ai du mal à comprendre comment obtenir une bonne couverture (ou même simplement comment mesurer la couverture intelligemment).
Beaucoup de réponses standard que j'ai rencontrées jusqu'à présent ne s'appliquent pas vraiment ici. Par exemple, "écrire du code testable" est un excellent conseil lorsque vous commencez à écrire du code, mais la refactorisation de millions de lignes de code accumulées sur 25 ans n'est pas dans la portée de ce projet, pas une tâche gérable pour une petite équipe et une impossibilité politique donnée les circonstances.
Je suis à la recherche de toutes les suggestions sur la façon d'obtenir une bonne couverture de test, comment mesurer la couverture et généralement comment aborder la transition des tests manuels mal documentés à une sorte de test automatique sensiblement complet.
Je ne suis pas un expert, donc il peut y avoir des fruits bas que j'ai négligés, en particulier si je ne connais pas les termes de recherche pertinents - si tel est le cas, un prodige doux dans la bonne direction peut très bien être un bon départ.
J'ai bien peur qu'il n'y ait pas de solution facile. "écrire du code testable" est vraiment la seule façon de le faire.
L'écriture de code testable n'est pas anodine et les tests de réajustement sont difficiles. De nombreuses avancées dans le codage moderne ont visé à rendre le code testable, avec d'autres avantages comme effet secondaire. La rédaction des tests est le plus simple!
Le commentaire de MrSmith est une bonne stratégie:
chaque fois qu'une paix de code est étendue/refactorisée/ajoutée/corrigée, ajoutez des tests pour cette partie. Au fil du temps, vous améliorerez la couverture de test de cette façon étape par étape.
Cela présente quelques avantages:
Bien qu'il s'agisse d'une mention redondante, toute personne lisant cette question sans avoir ce genre de problème doit se rappeler que le développement de logiciels dans la vie réelle est plein de cas de développement sous-standard pluriannuel effectué sous beaucoup de temps, ce qui entraîne des bases de code désordonnées , qui, néanmoins, fournissent superficiellement tout ce qui a été pratiquement demandé. Lorsque la vie vous remet plus de 25 ans de code accumulé sans tests, la fuite n'est pas toujours une option.
Tout ce que vous avez pour le moment, ce sont vos tests manuels. Bien que vous ne puissiez pas découvrir "par magie" ce qu'ils couvrent, vous devez vous libérer du temps passé manuellement tests, ce qui est probablement mieux dépensé pour documenter (ou améliorer, ou écrire des tests réels ).
Si votre seul "cadre" utilisable pour les tests est la véritable interface utilisateur, je vous suggère d'examiner GUI testing , qui est pratiquement basé sur la "simulation" de ce que vous faites lorsque vous utilisez le logiciel. Ainsi, vous pouvez générer des clics ici et là sur les différents éléments de l'interface utilisateur dans l'ordre que vous connaissez un testeur manuel. Vous devrez peut-être surmonter des problèmes tels que le chargement automatique de votre ensemble de données ou la définition d'options, mais, selon la base de code, vous pourrez peut-être y parvenir à la fin.
Bien que cela dépende fortement de votre infrastructure graphique actuelle, il peut donc être plus facile ou plus difficile à réaliser, je pense qu'une première tentative d'automatisation de votre méthodologie de test actuelle pourrait vous faire gagner du temps et même vous permettre d'écrire de nouveaux tests "manuels" automatisés. Jetez un oeil ici pour une liste d'outils de test GUI pour avoir une brève idée de ce qui est autour.
En dehors de tout cela, réponse de Robin Bennett est en effet de très bons conseils intemporels.
Peut-être un "coup de feu dans l'obscurité", mais si les choses sont très difficiles, je pense avoir rencontré quelque chose qui pourrait être utile. Comme je l'ai dit , l'une des seules choses qui peut aider à ce qui est testé, serait une trace continue de la pile d'appels ... Il pourrait être possible que l'outil de Valgrind nommé callgrind , un callgraph analyseur ( selon Wikipedia ), pourrait être utile pour cette raison . L'outil se décrit comme :
Callgrind est un outil de profilage qui enregistre l'historique des appels parmi les fonctions d'un programme exécuté sous forme de graphique d'appels. Par défaut, les données collectées sont composées du nombre d'instructions exécutées, de leur relation avec les lignes source, de la relation appelant/appelé entre les fonctions et du nombre de ces appels. Facultativement, la simulation de cache et/ou la prédiction de branche (similaire à Cachegrind) peut produire des informations supplémentaires sur le comportement d'exécution d'une application.
Cela semble prometteur et, en l'absence de toute autre meilleure alternative, je suggère d'essayer ce (ou tout autre outil similaire)! Une "trace" de graphe d'appel détaillée capturée lors de chaque test basé sur l'interface utilisateur permettrait de découvrir ce qui se passe réellement dans les coulisses.
(Une référence spéciale va à cette réponse , qui m'a dirigé dans cette direction).
Je pense que Vector Zita vous démarre dans la bonne direction avec les tests GUI, je vais ajouter cette réponse pour résoudre les problèmes de couverture de code.
Faites ce que Vector Zita a dit. Vous avez besoin de tests GUI. Selon la plate-forme sur laquelle cette application s'exécute, vous devriez pouvoir trouver des outils qui vous permettent d'automatiser l'interface utilisateur. Bien que vous ne souhaitiez pas tester l'interface utilisateur, vous pouvez toujours tester la fonctionnalité principale via l'interface utilisateur. Vous le faites simplement à un niveau beaucoup plus élevé. Ne pensez pas aux tests en termes d '"interface utilisateur" et de "fonctionnalités de base". Pensez-y en termes de tests d'acceptation des utilisateurs, de tests négatifs, de tests aux limites et de tests de boîte noire (beaucoup de mots à la mode googleable ici).
Tests d'acceptation par l'utilisateur couvre les principaux cas d'utilisation de l'application et peut être glané à partir de la documentation existante. Pensez "chemins heureux" à travers l'application. Ce sont d'excellents tests pour commencer. Ils sont plus petits et vous permettent de tester une plus grande partie de la base de code dans un court laps de temps d'exécution - un "test de fumée" en quelque sorte.
Tests négatifs couvrir les "chemins malheureux" à travers l'application, ce qui permettrait de tester des choses comme "vous ne pouvez pas une date future pour un date de naissance." Cela peut également être écrit à partir de la documentation des exigences existantes.
Tests aux limites sont préoccupés par les valeurs correctes et incorrectes dans une plage de valeurs, comme le prix doit être compris entre 0 et 50. Écrivez un test automatisé chacun pour savoir comment vous vous attendez à ce que l'application se comporte à -1, 0, 25 (une valeur moyenne raisonnable), 50 et 51. Encore une fois, consultez les exigences pour savoir quelles limites doivent être testées.
Tests de la boîte noire combler les lacunes où la documentation des exigences est manquante ou inexistante. Ces tests impliquent de jeter des données à l'interface utilisateur dans le format et la quantité que vous pouvez imaginer pour voir comment l'application se comporte. En cas de manque de documentation appropriée sur la façon dont la chose est censée fonctionner, écrivez en passant les tests de boîte noire pour au moins savoir comment cela fonctionne actuellement.
Écrivez tout cela sous forme de tests automatisés qui atteignent l'interface utilisateur. Ce sera beaucoup de travail. Les tests s'exécuteront plus lentement que les tests unitaires, mais au moins vous aurez avoir tests.
Vous pouvez commencer à refactoriser le code pour activer les tests unitaires appropriés après avoir écrit tous ces tests automatisés de l'interface utilisateur. Refactorisez ce code de 16 ans pour utiliser le principe de responsabilité unique ici, la séparation des préoccupations là-bas et une pincée d'injection de dépendance ici. Couvrez ce nouveau code correctement avec des tests unitaires. Validez que vous n'avez rien cassé en exécutant les tests automatisés existants (voir Red-Green-Refactor ).
Rincez et répétez jusqu'à ce que vous ayez réduit ce code vieux de 20 ans et l'avez remplacé par un nouveau code brillant. J'espère que cela permettra à quelqu'un d'autre à l'avenir de remplacer votre nouveau code brillant par un code plus récent et plus brillant à un rythme beaucoup plus rapide.
Cela ne vous donnera pas les mesures de couverture de code les plus précises au début. J'ai trouvé que les outils de couverture de code signalent une couverture de 0% lors de l'exécution des tests d'interface utilisateur ou une couverture de code de 99% avec très peu de compromis, selon la quantité de code d'application réutilisée comme code de test. Ce n'est qu'après une refactorisation suffisante de la base de code pour prendre en charge les tests unitaires que les outils de couverture de code vous donneront des chiffres plus précis.
Pour commencer, vous ne cherchez pas à savoir quelles lignes de code sont couvertes par un test et lesquelles ne le sont pas. Vous souhaitez couvrir autant de cas d'entrée et de sortie attendus que vous le pouvez, puis refactoriser la base de code de manière incrémentielle au fur et à mesure que vous en avez le temps, ou au fur et à mesure que vous devez modifier d'anciennes fonctionnalités ou corriger des bogues.