Dernièrement, nous avons entendu et lu de plus en plus sur le développement piloté par les tests. Ce domaine, cependant, comporte une série d’expressions et un jargon spécifique qui peuvent prêter à confusion pour les nouveaux venus. Cet article vous guidera à travers les définitions, les types de test et les pièces de test les plus courants. Des cas d'utilisation seront fournis et, si possible, du code en PHP sera également présenté..
Il y a quelques années, un nouveau programmeur a été embauché dans une équipe de développement. Comme tout autre nouveau venu, il était assez confus le premier jour. Alors qu'il écoutait les discussions autour de lui au bureau, de nombreux termes spécifiques à des tests ont été utilisés. Ce sont des expressions inconnues de notre nouveau programmateur fictif.
Heureusement pour lui, étant son premier jour de travail, les deux collègues ont ensuite été chargés de lui expliquer tout ce jargon. Ils ont commencé par une liste de termes liés au fonctionnement interne d’un cas de test.
Logiciel de test de logiciel est pratiquement un test automatisé. L'automatisation des tests existe depuis avant le PC; les premiers frameworks de tests automatisés sont apparus à l'époque des ordinateurs centraux et des consoles. Aujourd'hui, les tests automatisés sont la solution évidente. Pourquoi? Parce que les tests sont une tâche fastidieuse et répétitive, quelque chose de mal adapté aux êtres humains. Les tests automatisés sont considérablement plus rapides et plus précis que les tests manuels. Et non, cela n’élimine pas le testeur humain ou une équipe d’assurance qualité. Cela leur permet simplement de faire un travail plus adapté, et leur permet de bien le faire.
Tout test doit pouvoir être divisé en quatre parties:
Nous concevons chaque test de manière à avoir quatre phases distinctes exécutées en séquence: configuration de la fixture, exercice SUT, vérification du résultat et démontage de la fixture. - Modèles de test xUnit: Code de test de refactoring, par Gerard Meszaros
Un appareil représente toutes les informations nécessaires au test pour pouvoir être exercé. Un appareil peut être aussi simple que créer un objet simple, comme $ protectedObject = new RealObject ();, ou quelque chose d'aussi compliqué que de remplir des bases de données et de démarrer des interfaces utilisateur.
Tout ce qu'un système sous test (SUT) doit avoir en place pour pouvoir l'exercer dans le but de vérifier son comportement. - Modèles de test xUnit: Code de test de refactoring par Gerard Meszaros
Vous avez probablement observé ce terme récurrent. Les programmeurs y font généralement référence SUT. Il représente tout ce qui doit être testé. Selon le type de test (voir ci-dessous pour les types de test), le SUT peut être composé de plusieurs méthodes, d'une méthode ou d'une classe à l'ensemble du système..
Quelle que soit la chose que nous testons. Le SUT est toujours défini du point de vue du test. - Modèles de test xUnit: Code de test de refactoring, par Gerard Meszaros
Dès son deuxième jour de travail, notre programmeur a écrit son premier test. C'était plus difficile que prévu. Pour l'écrire, il avait besoin d'un cadre de test, et il devait créer un cas de test puis lancez tous les Méthodes d'essai. Il y avait aussi une poignée d'étranges dépendances dont il avait besoin de comprendre. Il semblait que l'apprentissage de DOC était à l'heure.
Un framework de test est une application spécialement conçue pour tester le code dans un langage spécifique. Le concept de cadre de test Kent Beck a été le pionnier au début des années 90. Son travail a ensuite conduit à un framework pour SmallTalk, appelé SmalltalkUnit, et renommé ensuite SUIT.
Smalltalk a souffert parce qu'il lui manquait une culture de test. Cette colonne décrit une stratégie de test simple et un cadre pour la prendre en charge. La stratégie et le cadre de test ne sont pas destinés à être des solutions complètes, mais plutôt un point de départ à partir duquel des outils et des procédures industriels peuvent être construits. - Tests Smalltalk simples: avec motifs, par Kent Beck
C'était le premier xUnit cadre, et a défini le concept de base des tests et les termes présentés ci-dessus. Aujourd'hui, presque tous les langages de programmation proposent leur version de ce framework: PHPUnit pour PHP, JUnit pour Java, ShUnit pour UNIX Shell Scripts, etc. Vous seriez surpris de savoir combien de choses peuvent être testées aujourd'hui et combien de tests peuvent être automatisés..
À l’origine, Kent Beck avait défini un «cas de test» comme étant la plus petite unité d’essai..
Lorsque vous parlez à un testeur, la plus petite unité de test dont il parle est un cas de test. TestCase est un objet utilisateur, représentant un cas de test unique. - Test Smalltalk simple: avec des motifs de Kent Beck
Ces jours-ci, nous utilisons méthode d'essai définir cette plus petite partie et un cas de test fait généralement référence à un ensemble de méthodes de test associées. Par exemple, une situation typique est lorsque nous testons notre code par unité et qu'un scénario de test fait référence à la totalité des méthodes de test testant une classe particulière ou la plus petite unité de notre langage de programmation. Un cas de test, à plusieurs reprises, est simplement appelé: "un examen."
Une méthode de test est la plus petite partie d'une architecture de test. Une méthode de test est l’unité constituée des éléments définis ci-dessus: configuration / exercice / vérification / démontage. C'est la partie essentielle de tout test; celui que fait le travail.
Une méthode de test est une procédure définitive qui produit un résultat de test. - Manuel de forme et de style, ASTM, 2012
C’était facilement l’un des nouveaux termes les plus déroutants pour notre nouveau programmeur. Il représente toutes les autres classes et composants système dont notre SE a besoin pour fonctionner correctement. Mais, aussi, le DOC doit fournir des méthodes spécifiques qui nous permettent de l'observer et de le tester. Les concepts de railleur et double test sont fortement liés au DOC.
Une classe individuelle ou un composant à gros grains dont dépend le système sous test (SUT). La dépendance est généralement une délégation via des appels de méthodes. - Modèles de test xUnit: Code de test de refactoring, par Gerard Meszaros
Peu après avoir écrit ses premiers tests, le nouveau gars a réalisé qu'il testait différentes parties logiques de l'application. Parfois, il est préférable de tester une petite partie de manière isolée; d'autres fois, il est nécessaire de tester un groupe d'objets ensemble et leur façon de se parler; et d'autres fois, vous devez tester l'ensemble du système. Les choses semblaient plus compliquées qu'on ne le supposait auparavant; alors notre programmeur a continué et a lu un livre, et un autre, et un autre, et, enfin, il a compris.
La pyramide de test a été définie pour la première fois dans le livre, Réussir avec le développement logiciel agile en utilisant Scrum, par Mike Cohn, puis bientôt adopté par la communauté des logiciels.
La pyramide représente les trois couches de test principales: interface utilisateur, service et unité..
le Couche d'interface utilisateur représente le niveau de test le plus élevé: lorsque le système s'exerce via l'interface utilisateur et que l'application entière est testée comme un seul. Cette couche devrait représenter la plus petite quantité dans notre multitude de tests.
le Couche de service contient plusieurs types de tests différents. Il s’agit principalement de la communication interne des modules et du bon fonctionnement de l’API externe (interface de programmation) d’une application. Il devrait y avoir plusieurs tests de ce type dans nos suites, mais ils ne devraient pas constituer une base pour nos tests. Ces tests entraînent généralement plusieurs parties de l’application et sont donc assez lents. Ils doivent être exécutés aussi souvent que possible, mais pas à chaque sauvegarde du code. Probablement à chaque construction du système ou lorsqu'un commit se produit dans le système de gestion de versions.
le Couche unitaire fait référence à des tests exécutant les plus petites unités possibles de notre code de manière totalement isolée. Ces tests devraient représenter la grande majorité des tests. Ils doivent être très rapides (1-4 millisecondes / test) et doivent être exécutés aussi souvent que possible. Le développement piloté par les tests (TDD) est un bon exemple de la façon de maximiser l'utilisation des tests unitaires..
Sur la base de l'exemple ci-dessus, la communauté a mis au point plusieurs versions plus détaillées de la pyramide de test. Celui que je considère comme le meilleur peut être vu dans l'image ci-dessous.
Les trois couches principales peuvent être clairement distinguées, mais la couche centrale est plus détaillée. Au fil du temps, la communauté des logiciels a découvert et défini plusieurs nouvelles méthodes de test. Certains d'entre eux ont été inclus sur la pyramide.
Notez s'il vous plaît: Les techniques de test automatisées et les frameworks évoluent encore très rapidement. C'est pourquoi, comme vous pouvez le voir ci-dessous, certaines expressions ne sont pas encore claires et il existe plusieurs termes pour les mêmes définitions en fonction de la communauté qui les a promus..
Un test unitaire représente le test de la plus petite unité dont le langage de programmation le permet. En programmation orientée objet, il s'agit de classes / objets. Dans d'autres langues, il peut s'agir de petits modules ou même de fonctions / procédures.
UNE tester dans ces définitions se réfèrent à la même chose qu'un cas de test représente.
Un test qui vérifie le comportement d'une petite partie du système global. Ce qui transforme un test en test unitaire est que le système sous test (SUT) est un très petit sous-ensemble du système global et peut être méconnaissable pour une personne qui n'est pas impliquée dans la construction du logiciel. - Modèles de test xUnit: Code de test de refactoring par Gerard Meszaros
Les tests unitaires représentent la grande majorité des tests écrits par un programmeur. Oui, c’est vrai: les tests unitaires sont la plupart du temps écrits par des programmeurs. Les tests unitaires aident les programmeurs à développer l’application, à prévenir les bugs, les fautes de frappe et les régressions courants. Ce sont des tests réalisés par des programmeurs pour des programmeurs.
C'est pourquoi les tests unitaires sont de nature plus technique et plus cryptique. Ils sont là pour aider les programmeurs à écrire un meilleur code. quand quelque chose échoue au niveau des tests unitaires, le programmeur a généralement du mal à résoudre le problème..
Comme son nom l'indique, les tests de composants sont écrits pour des morceaux un peu plus volumineux de l'application. Un test de composant exerce généralement un module entier ou un groupe d’unités interdépendantes sur le plan logique..
Le composant est une conséquence d'une ou de plusieurs décisions de conception, bien que son comportement puisse également être attribué à certains aspects des exigences. - Modèles de test xUnit: Code de test de refactoring par Gerard Meszaros
Certes, un test de composant exerce plus de code qu'un test unitaire. Il peut également tester la manière dont certaines unités travaillent ensemble et se parlent.
Un composant peut également être associé à une exigence ou à une partie d'une exigence. Cela signifie qu'un test de composant n'est pas réservé aux programmeurs. Les chefs d’équipe, maîtres de scrum, architectes et autres personnes impliquées sur le plan technique sont certainement intéressés par les modules, par leur organisation et parfois même par leur fonctionnement interne. Ces personnes ne sont pas nécessairement familiarisées avec un langage de programmation spécifique. Le test doit se concentrer davantage sur le comportement et définir les attentes de manière plus compréhensible..
Par exemple, un test unitaire peut avoir un message d'erreur indiquant que:
TestFileAccessCanWriteToAFile: Échec de l'affirmation de la présence du fichier '/ tmp / testfile' sur le système..
Un tel message ne serait d'aucune utilité pour un architecte, un responsable ou un chef d'équipe. Un test de composant peut échouer avec une erreur plus descriptive:
Test d'administration de compte: Échec lorsque nous avons tenté de spécifier 0 (zéro) comme le total des fonds d'un utilisateur sur son compte..
Un tel test exerce une fonctionnalité de niveau supérieur. D'après le message d'erreur ci-dessus, plusieurs couches de communication et classes / objets peuvent être impliqués dans l'opération de spécification d'un montant correspondant au total dans le compte Someones..
Ce type de test prend plusieurs modules et vérifie leur intégration. Il vérifie si les API du module interne sont compatibles et fonctionnent comme prévu.
Le terme, cependant, permet un large éventail d'utilisations possibles. Certaines communautés de logiciels associent étroitement les tests d’intégration aux tests de fonctionnement de notre application au sein du support qu’elle doit exécuter. En d'autres termes, comment il s'intègre dans le système supérieur.
D'autres définissent le test d'intégration à différents niveaux: tout ce qui définit la communication entre deux éléments peut être considéré comme une intégration. Ces éléments peuvent être des unités, telles que des classes, des modules ou même des parties fonctionnelles supérieures du logiciel..
Il n’existe pas de définition unanimement acceptée du terme test d’intégration.
L'interface graphique d'une application communique avec le logiciel via l'API du logiciel. Tester à ce niveau nécessite beaucoup de code et peut durer relativement longtemps..
L'API est le moyen par lequel un autre logiciel peut invoquer une fonctionnalité. - Modèles de test xUnit: Code de test de refactoring par Gerard Meszaros
En programmation orientée objet, ces API sont définies par les méthodes publiques des classes. Toutefois, si nous examinons un schéma de conception d'architecture de haut niveau, la signification de l'API peut être limitée aux méthodes publiques des classes fournissant des fonctionnalités à travers les limites de la logique applicative. Ces classes de limites représentent l’API et nous devons vérifier que, lorsque nous les appelons et les utilisons, le système se comporte comme prévu..
Habituellement, ces tests sont effectués périodiquement et prennent beaucoup de temps..
Il ne devrait y avoir que quelques rares cas où vous souhaitez tester la présentation d'une application. Il n'y a vraiment pas de logique dans la couche d'interface graphique, juste une présentation.
Tester si un bouton est vert ou rouge, ou s'il a 30px
en largeur est inutile, et est trop d'un overhead. Alors ne sautez pas dans le test de vos points de vue. Si quelque chose ne va pas du tout avec l'interface graphique, cela sera observé lors de la phase de test manuel exploratoire..
Les vues de test ne doivent faire que deux choses: tester la présentation conditionnelle et vérifier que l'API attendue est appelée.
Sauter dans le test de vos points de vue peut être tentant. Non! Testez uniquement ce que vous pensez peut échouer. Tester uniquement pour les valeurs ou les appels de fonction. Ne vérifiez jamais les éléments de l'interface graphique ou leurs propriétés. Utilisez autant que possible REGEX pour faire correspondre les chaînes et vérifier les mots clés non susceptibles de changer.
Par exemple, le pseudo-code suivant est un mauvais exemple pour tester la présence d'une chaîne à l'écran..
function testItCanTellTheNameOfTheUser () // une logique de code de rendu ici $ renderName = $ this-> renderName (); $ this-> assertEquals ('L'utilisateur porte le nom'. $ renderName ['first']. ". $ renderName ['last']. '.'))
Non seulement ce test est difficile à lire, mais il recherche une phrase exacte - quelque chose comme: "L'utilisateur a le nom John Doe.", y compris la ponctuation! Pourquoi est-ce mauvais? Parce que quelqu'un peut facilement changer la forme de cette phrase sans en changer le sens.
Et si notre client a besoin Nom Prénom formulaire à présenter? Cela ferait échouer notre test. Nous devons nous demander: le test doit-il échouer? Avons-nous changé la logique du logiciel? Je dis non, ça ne devrait pas échouer. Le nom serait toujours présent sur l'écran; l'ordre des deux parties serait simplement différent. Ceci est un test plus approprié.
function testItCanTellTheNameOfTheUser () // une logique de code de rendu ici $ renderName = $ this-> renderName (); $ this-> assertRegExp ($ renderName ['premier'], $ renderName); $ this-> assertRegExp ($ renderName ['last'], $ renderName);
Cela garantit maintenant que le nom est présent, mais peu importe la construction lexicale. Quelqu'un pourrait changer la phrase initiale en quelque chose, comme Don, John est le nom de l'utilisateur actuel. La signification restera la même et le test passera toujours correctement!
Après environ un mois de travail, notre nouveau programmateur fictif se rend compte que même si la pyramide est plutôt cool, elle n’est pas complète. Parfois, un ou deux tests différents doivent être exécutés - et ils sont assez difficiles à placer sur la pyramide.
Ce sont l'un des tests les plus controversés. Selon le type de livres que vous lisez, les tests d’acceptation peuvent être qualifiés de Tests fonctionnels
ou
ou
ou
Chaque nom provient d'une communauté ou d'un auteur différent. Personnellement, je préfère Tests d'acceptation ou Tests de bout en bout.
Un test d'acceptation vérifie le comportement d'une tranche de la fonctionnalité visible de l'ensemble du système. - Modèles de test xUnit: Code de test de refactoring par Gerard Meszaros
Un tel test fera quelque chose sur l'interface graphique. Le changement se produira dans tout le système. Les données seront enregistrées dans la base de données ou le système de fichiers. La communication réseau sera faite. Enfin, l'interface graphique sera vérifiée pour la réponse du système. De tels tests tentent d'imiter complètement un utilisateur.
Les tests d'acceptation sont étroitement liés aux parties prenantes de notre application. Elles sont généralement définies dans le langage de l'entreprise et, en cas de problème, toute une fonctionnalité est considérée comme caduque. Ces tests servent également à définir les fonctionnalités de haut niveau de l'application..
Habituellement, ils sont écrits par le contrôle qualité et la gestion et mis en œuvre par les programmeurs. À l'origine, ils ont été inventés comme un pont entre la gestion et la production. Pour certaines situations, ils ont réussi. Le langage des tests est suffisamment souple pour être écrit et compris par des personnes ne participant pas directement à l'écriture de logiciels..
Il existe des cadres spéciaux pour ces tests, comme Fitness, Selenium, Watir, Cucumber et autres.
Ce sont des cas plus particuliers et ne sont pas utilisés trop souvent. Vous pouvez les utiliser parfois dans des langages orientés objet lorsque les interfaces et l'héritage doivent être testés. Le test garantit essentiellement qu’une classe implémente réellement toutes les interfaces dont elle dispose..
Les tests de contrat expliquent comment une classe devrait étendre une super-classe ou implémenter une interface. - J. B. Rainsberger
Dans certaines applications, le terme Contrat est utilisé pour un autre type de test. Cette deuxième définition du test de contrat vérifie si le contrat entre notre application et un composant tiers sur lequel nous dépendons est respecté. Ces tests exercent le code actuel et le code tiers en s'assurant que les résultats sont conformes aux attentes.
Après des vacances bien méritées, notre programmeur pas si junior est de retour au travail. C'était son premier congé et il se sentait plein de nouveaux pouvoirs pour écrire des tests et du code. Au bout de six mois, il se sent plutôt bien au travail; il s’est bien intégré à l’équipe et écrit un très bon code. Mais de temps en temps, il a un sentiment frustrant. Exécuter cinq types de suites de tests différents dans un ordre strictement défini chaque soir est ennuyeux et propice aux erreurs.
Ensuite, il y a une autre discussion étrange entre son chef d'équipe et la direction. Ils parlent de C.I. et CD.. Qu'est-ce que cela pourrait signifier? C'était trop cryptique pour que notre nouveau programmeur comprenne. Quelques semaines plus tard, un message était diffusé à l’ensemble de la société: "S'il vous plaît, ne lancez plus vos tests du soir. Nous avons C.I.!. Pour en savoir plus, il s'est adressé à son chef d'équipe et a demandé: "Qu'est-ce que CI et CD?".
Les équipes qui s'appuient fortement sur des tests automatisés ont besoin d'un moyen d'exécuter tous ces tests de manière organisée et efficace. Un système d'intégration continue aide à cela.
L’intégration continue est une pratique de développement logiciel dans laquelle les membres d’une équipe s’intègrent fréquemment dans leur travail. Chaque personne s’intègre au moins quotidiennement, ce qui conduit à de multiples intégrations par jour. Chaque intégration est vérifiée par une génération automatisée (y compris un test) pour détecter les erreurs d'intégration aussi rapidement que possible. - Martin Fowler
Sur la base de cette définition, le processus d'intégration continue permet d'exécuter nos tests sans intervention humaine. Dans la définition, une intégration fréquente est illustrée quotidiennement, mais je peux vous dire que c’est vraiment bien de travailler sur une base logicielle qui est automatiquement testée sur chaque commit. S'engager fréquemment signifie que toute modification effectuée doit être validée afin que vous puissiez en avoir des dizaines le même jour. Chaque validation déclenchera un test complet du système et vous obtiendrez un email de qualité, vert ou rouge, en fonction du résultat, en quelques minutes seulement. Si le courrier est vert, le produit est en principe immédiatement expédiable..
Ce n'est pas tellement lié au test, mais au CI. Bien que CI vous permette d’obtenir un système livrable, les mises à jour sont toujours effectuées périodiquement et manuellement..
La livraison continue automatise toutes les étapes du code au client.
Une fois que tout est fait au niveau des éléments de configuration, la livraison continue va plus loin et construit les kits d'installation de logiciels, les publie au besoin et peut même déclencher des procédures de mise à jour à distance sur les clients. L’objectif d’un tel système est de livrer le produit le plus rapidement possible à l’utilisateur final. Il dépend fortement des tests automatisés et, si tous les tests réussissent, le produit est livré. Période.
Même si, dans certaines situations, cela peut sembler très attrayant, dans la plupart des applications, cela reste trop dangereux. Habituellement, tout système décemment critique doit passer par une session de test manuel exploratoire avant la livraison..
Certaines parties d'une procédure de test sont tout simplement trop difficiles, voire impossibles, à automatiser. C'est pourquoi un Test manuel exploratoire La session est généralement effectuée avant chaque version du logiciel. Ces tests peuvent être réalisés par des testeurs spécialisés ou par les programmeurs en fonction de la structure de l'équipe et de l'entreprise..
Le test manuel implique de l'imagination. Les êtres humains fouinent dans le système pour s'assurer qu'il fonctionne et a l'apparence souhaitée.
Certaines équipes considèrent le test manuel un << Break it if you can! >> concept.
Vous ne pouvez pas éviter de tester le jargon. J'espère que cet article a mis en lumière les différences entre les différentes formes de test. Des questions? Demandez ci-dessous!