J'ai essayé d'écrire des tests unitaires d'interface utilisateur pour mes applications GUI et je suis confronté au problème que, même si elles fonctionnent bien lorsque je les écris au départ, elles se révèlent fragiles et se cassent chaque fois que la conception change (c'est-à-dire, assez souvent). J'ai du mal à trouver un ensemble de directives qui m'amèneraient à avoir des tests unitaires maintenables pour l'interface graphique.
Pour l'instant, une chose que j'ai découverte est que les tests disant "ce composant devrait montrer ses données d'entrée quelque part" sont bons (et c'est très facile avec HTML). Les tests vérifiant un état spécifique d'une partie spécifique du composant sont généralement fragiles. Les tests qui vont comme cliquer-cliquer-cliquer-attendre, qui essaient de suivre le comportement de l'utilisateur et la logique métier sous-jacente (qui est la partie la plus importante) se révèlent généralement fragiles. Comment écrire de bons tests?
Pour être plus précis, j'aimerais connaître certains modèles sur ce que je pourrais tester dans mon interface utilisateur, pas exactement comment le tester. Les conventions de dénomination et les identifiants fixes sont bons, mais ne résolvent pas le problème principal, qui est que les interfaces graphiques changent beaucoup. Je voudrais tester les comportements les moins susceptibles de changer. Comment trouver la bonne chose à tester?
Réponses:
Un problème commun avec les tests GUI ... La raison principale pour laquelle ces tests sont considérés comme fragiles est qu'ils ne peuvent pas survivre à un changement dans l'interface graphique qui n'est pas un changement dans les exigences . Vous devez vous efforcer de structurer votre code de test de manière à ce qu'un changement dans l'interface graphique soit isolé à un seul endroit dans les tests.
À titre d'exemple, considérons un test libellé comme suit:
Beaucoup d'espace ici pour que ce test se brise lorsque vous retravaillez l'interface, même si l'exigence de validation reste.
Maintenant, mettons cela dans un petit libellé alternatif:
Le test est le même, les exigences sont les mêmes, mais ce type de test survivra à une refonte de votre interface utilisateur. Vous devrez évidemment changer de code, mais le code sera isolé. Même si vous avez dix ou vingt tests de ce type pour votre page de profil et que vous déplacez votre logique de validation affichant les messages d'erreur de javascript-alerts à jquery-popups par exemple, il vous suffit de modifier la seule partie de test qui vérifie les messages d'erreur.
la source
C'est un problème commun. Je ferais attention à:
Comment vous nommez les éléments
Utilisez id ou classe css pour identifier les éléments. Privilégiez l'utilisation de l'ID CSS lorsque l'objet est unique. Tenez compte du cadre que vous utilisez, par exemple avec Ruby on Rails, l'
name
attribut est attribué automatiquement et peut (de manière non intuitive) être meilleur que d'utiliser l'ID ou la classe CSSComment vous identifiez les éléments.
Évitez les identificateurs de position comme
table/tr/td/td
en faveur de formes telles quetd[id="main_vehicle"
outd[class='alternates']
. Envisagez d'utiliser des attributs de données lorsque cela est approprié. Encore mieux, essayez d'éviter les balises de mise en page telles que<td>
tout à fait.Pour ce qui précède, vous pouvez soit ajouter une étendue et l'utiliser, par exemple,<span id="main_vehicle">
soit un sélecteur générique tel que*[id="main_vehicle"]
où*
pourrait maintenant être un div, une étendue, un td, etc.Utiliser des attributs de données spécifiques au test qui ne sont utilisés que pour qa et les tests.
Évitez la qualification inutile des éléments. Vous pourriez vous retrouver en utilisant les éléments suivants:
body.main div#vehicles > form#vehicle input#primary_vehicle_name
Cependant, cela nécessite que le champ de saisie reste dans un formulaire avec un identifiant exact de véhicule et sur une page avec un corps qui a une classe de principal et un div avec un identifiant de véhicules qui a un enfant immédiat d'un formulaire avec un identifiant de véhicule. Tout changement à l'une de ces structures et le test échoue. Dans ce cas, vous pourriez constater que
input#primary_vehicle_name
est suffisant pour identifier l'élément de manière unique.
Évitez les tests qui font référence à du texte visible. Le texte de la page qui est présenté à l'utilisateur change généralement au fil du temps au fur et à mesure que le site est mis à jour et mis à jour, utilisez donc des identifiants tels que css id et css class ou des attributs de données. Des éléments tels que
form
,input
etselect
utilisés dans les formes sont aussi bonnes parties d'éléments d' identification, généralement en combinaison avec id ou de classe, par exemple ,li.vehicle
ouinput#first-vehicle
vous pouvez également ajouter vos propres identifiants, par exemple<div data-vehicle='dodge'>
. De cette façon, vous pouvez éviter d'utiliser les ID d'élément ou les classes, qui sont susceptibles d'être modifiés par les développeurs et les concepteurs. Au fil du temps, j'ai trouvé qu'il valait mieux travailler avec les développeurs et les concepteurs et s'entendre sur les noms et les étendues. C'est difficile.Comment les données fixes sont maintenues.
Semblable à l'identification d'éléments réels, essayez d'éviter que le sélecteur en ligne codé en dur n'identifie les valeurs en faveur des objets de page - de petits morceaux de texte qui sont conservés dans des variables ou des méthodes et peuvent ainsi être réutilisés et également gérés de manière centralisée. Exemples de variables javascript suivant ce modèle pour les valeurs codées en dur:
Plus d'informations sur les objets de la page sur le sélénium wiki et les documents sur le sélénium
Communication avec les développeurs.
Indépendamment de l'approche technique en termes de «développeurs apportent des modifications et cassent l'automatisation de l'assurance qualité» qui est un problème de workflow. Vous devez vous assurer que: tout le monde est la même équipe; le développeur exécute les mêmes tests intégrés; les normes sont convenues et suivies par les deux groupes; la définition de done comprend l'exécution et éventuellement la mise à jour des tests d'interface utilisateur; Les développeurs et les testeurs s'appuient sur des plans de test et assistent tous deux au toilettage des tickets (si vous faites Agile) et parlent des tests d'interface utilisateur dans le cadre du toilettage. Vous devez vous assurer que l'approche et la stratégie que vous utilisez pour nommer sont coordonnées avec les développeurs d'applications. Si vous n'obtenez pas sur la même page, vous aimerez affronter la dénomination des objets. Quelques exemples de méthodes d'objet de page que j'ai récemment créées pour un projet ruby:
Voici les mêmes objets de page que les variables javascript:
la source
La raison pour laquelle les gens ont développé des choses comme MVC, MVP et le présentateur, et des modèles de conception similaires, était de séparer la logique métier de l'interface utilisateur.
De toute évidence, la partie vue ne peut être testée qu'en démarrant le programme et en vérifiant ce qu'elle montre - en d'autres termes, elle ne peut être testée que dans des tests d'acceptation.
D'un autre côté, le test de la logique métier peut être effectué dans des tests unitaires. Et c'est la réponse à votre question. Testez tout dans le modèle, et si vous le pouvez et le souhaitez, vous pouvez également tester le code du contrôleur.
Cela ne peut se produire que lorsque vous avez des exigences changeantes. Lorsqu'une exigence change, il n'y a aucun moyen de la contourner, sauf pour modifier le code. Si vous parvenez à créer une bonne conception et une bonne architecture, le changement ne se propagera pas à de nombreux endroits.
la source
Les tests d'interaction GUI ne doivent pas être plus ou moins fragiles que tout autre type de test. C'est; si votre application change d'une manière ou d'une autre, les tests doivent être mis à jour pour refléter cela.
A titre de comparaison:
Test de l'unité
Original :
validateEmail()
devrait jeter unInvalidData
exception. Ce qui est correctement couvert dans votre test unitaire.Changement :
validateEmail()
devrait lever uneInvalidEmail
exception. Maintenant, votre test est incorrect, vous le mettez à jour et tout redevient vert.Test GUI
Original : la saisie d'un e-mail non valide entraînera une boîte d'erreur contextuelle contenant "Données non valides saisies". Détecté correctement par vos tests.
Modification : la saisie d'un e-mail non valide entraînera une erreur en ligne contenant "E-mail non valide entré". Maintenant, votre test est incorrect, vous le mettez à jour et tout redevient vert.
N'oubliez pas que vous testez les entrées et les sorties - un comportement bien défini. Que ce soit un test GUI ou un test unitaire ou un test d'intégration, etc.
la source